(三)王道機試指南___日期類計算
阿新 • • 發佈:2018-12-19
算例1
-
題目描述
-
解題思路
①解決這類區間問題有一個統一的思想——把原區間問題統一到確定的區間問題,即把問題統一到特定的日期與一個原點(如0000年1 月1日)的天數差,然後將兩個天數差相減,這有一個巨大的好處,就是預處理,我們可以在程式真正開始處理輸入資料之前,預處理出所有日期與原點日期之間的天數差並儲存起來。當資料真正開始輸入時,我們只需要用O(1)的時間複雜度將儲存的資料讀出,稍加處理便能得到答案。(預處理是一種空間換時間的重要手段)
②閏年判斷:year%100!=0&&year%4==0 || year%400==0
-
解題程式碼
#include <stdio.h> #define ISYEAP(x) x % 100 != 0 && x % 4 == 0 || x % 400 == 0 ? 1 : 0 // 定義巨集判斷是否是閏年,方便計算每月天數 int dayOfMonth[13][2] = { 0,0, 31,31, 28,29, 31,31, 30,30, 31,31, 30,30, 31,31, 31,31, 30,30, 31,31, 30,30, 31,31 }; //預存每月的天數,注意二月配合巨集定義作特殊處理 struct Date { //日期類,方便日期的推移 int Day; int Month; int Year; void nextDay() { //計算下一天的日期 Day++; if (Day > dayOfMonth[Month][ISYEAP(Year)]) { //若日數超過了當月最大日數 Day = 1; Month++; //進入下一月 if (Month > 12) { //月數超過12 Month = 1; Year++; // 進入下一年 } } } }; int buf[5001][13][32]; //儲存預處理的天數 int Abs(int x) { //求絕對值 return x < 0 ? -x : x; } int main() { Date tmp; int cnt = 0; //天數計數 tmp.Day = 1; tmp.Month = 1; tmp.Year = 0; //初始化日期類物件為0年1月1日 while (tmp.Year != 5001) { //日期不超過5000年 buf[tmp.Year][tmp.Month][tmp.Day] = cnt; //將該日與0年1月1日的天數差儲存起來 tmp.nextDay(); //計算下一天日期 cnt++; //計數器累加,每經過一天計數器即+1,代表與原點日期的間隔又增加一天 } int d1, m1, y1; int d2, m2, y2; while (scanf("%4d%2d%2d", &y1, &m1, &d1) != EOF) { scanf("%4d%2d%2d", &y2, &m2, &d2); //讀入要計算的兩個日期 printf("%d\n", Abs(buf[y2][m2][d2] - buf[y1][m1][d1]) + 1); //用預處理的資料計算兩日期差值, 注意需對其求絕對值 } return 0; }
-
注意點
①案例程式碼和自己寫的程式碼相比有很多的優點,比如巨集定義閏年判斷,DayOfMonth二維陣列的定義,儲存預處理的天數buf陣列的引入,以及整個思路的轉換就避免了很多閏年天數月數的計算,十分值得學習!!
②記!輸入時在%d之間插入數字來讀取特定位數的數字,輸出時可以在數字前加0表示空位補0(%04d)
③記!善於運用多維陣列儲存資料,例如月份、天數、星期
④buf陣列需要耗費大量記憶體,若在main函式中定義,容易導致棧溢位,因此凡是涉及此類需要開闢大量記憶體空間的情況,我們都必須在函式體外定義,即定義為全域性變數,或者在函式中使用malloc等函式動態申請變數空間
算例2
-
題目描述
-
解題思路
①利用上題中buf思想,由於星期是迴圈的,故可以用已知的日期星期號推輸入日期的星期號
-
解題程式碼
#include <stdio.h> #include <string.h> #define ISYEAP(x) x % 100 != 0 && x % 4 == 0 || x % 400 == 0 ? 1 : 0 int dayOfMonth[13][2] = { 0,0, 31,31, 28,29, 31,31, 30,30, 31,31, 30,30, 31,31, 31,31, 30,30, 31,31, 30,30, 31,31 }; struct Date { int Day; int Month; int Year; void nextDay() { Day++; if (Day > dayOfMonth[Month][ISYEAP(Year)]) { Day = 1; Month++; if (Month > 12) { Month = 1; Year++; } } } }; int buf[3001][13][32]; char monthName[13][20] = { "" , "January" , "February" , "March" , "April" , "May" , "June" , "July" , "August" , "September" , "October" , "November" , "December" }; //月名每個月名對應下標1到12 char weekName[7][20] = { "Sunday" , "Monday" , "Tuesday" , "Wednesday" , "Thursday" , "Friday" , "Saturday" }; //周名每個周名對應下標0到6 int main() { Date tmp; int cnt = 0; tmp.Day = 1; tmp.Month = 1; tmp.Year = 0; while (tmp.Year != 3001) { buf[tmp.Year][tmp.Month][tmp.Day] = cnt; tmp.nextDay(); cnt++; } //以上與上題一致,預處理出每一天與原點日期的天數差 int d, m, y; char s[20]; while (scanf("%d%s%d", &d, s, &y) != EOF) { for (m = 1;m <= 12;m++) { if (strcmp(s, monthName[m]) == 0) { break; //將輸入字串與月名比較得出月數 } } int days = buf[y][m][d] - buf[2012][7][16]; //計算給定日期與今日日期的天數間隔(注意可能為負) days += 1; //今天(2012.7.16)為星期一,對應陣列下標為1,則計算1經過days天后的下標 puts(weekName[(days % 7 + 7) % 7]); //將計算後得出的下標用7對其取模,並且保證其為非負數, 則該下標即為答案所對應的下標, 輸出即可 } return 0; }
-
注意點
①關於日期、月份數的儲存可以使用陣列,而不是switch-case