日期處理問題真是讓人感到頭疼,需要非常細心的去處理細節。(但是看了《算法筆記》上的解法,我覺得方法才是最重要的……)
下面以codeup上的一道題來介紹相關問題,以及從中習得的一些新的知識點。題目如下:
題目描述有兩個日期,求兩個日期之間的天數,如果兩個日期是連續的我們規定他們之間的天數為兩天。輸入有多組數據,每組數據有兩行,分別表示兩個日期,形式為YYYYMMDD輸出每組數據輸出一行,即日期差值樣例輸入2013010120130105樣例輸出5非常常規的一道題目,驗證了“細節處理非常重要”這句話,但是更加驗證了“方法更重要”這句話。
我想到的是用之前剛剛學會的sscanf 來處理輸入,如下:
char time1[10],time2[10];scanf("%s",time1);scanf("%s",time2);int year1,month1,day1;int year2,month2,day2;sscanf(time1,"%4d%2d%2d",&year1,&month1,&day1);sscanf(time2,"%4d%2d%2d",&year2,&month2,&day2);這種方法乍一看是可以的,不過事實上也的確可以。我們獲取輸入的目的無非就是想要把年、月、日拿出來,而這種方法已經達到了這樣的一個目的。
不過為了而后面的程序,我們必須把time1和time2分出個大小來,而這種拿法剛開始我認為在比較time1和time2大小上非常麻煩,需要有很多的分支判斷。再寫這篇文章的時候才猛然醒悟,類比后面的方法,此處的比較大下可以直接用串比較函數(strcmp函數)進行比較即可,親測可行。
下面給出《算法筆記》上處理輸入的方法,自己沒想到上面所說的字符串比較方法之前,覺得這個方法真實無比的巧妙??!代碼如下:
int time1, time2; //time1存小時間,time2存大時間int year1,month1,day1, year2,month2,day2;scanf("%d%d",&time1, &time2) if(time1 > time2){//保證time1存小時間,time2存大時間 int temp = time1; time1 = time2; time2 = temp; } // 除法(10^(n+1))拿到前面的n位數,模拿到后面n位數 year1 = time1/10000; month1 = time1%10000/100; day1 = time1%100; year2 = time2/10000; month2 = time2%10000/100; day2 = time2%100;從代碼可以看出,直接用兩個整形變量拿到time1和time2,之后用除法和取模運算,拿到相應的位數。
經過反思,我發現我之所以沒有想到這種方法,根本原因是在于對除法和取模運算的本質掌握不夠透徹,從這里得出一個結論:
結論:除法和取模運算可以用來獲取一個整形數的對應的位數,且存在如下規律:- 若想獲取整數X的前N位,則用X/10(N+1)- 若想獲得整數X的后N位,則用X%10(N+1)這么一來,這兩種方法可以說是殊途同歸了,關鍵點還是回到了如何計算兩年的時間差。
首先是我自己的第一想法,比如輸入為20160328和20110118這兩個日期,我的想法是分成三部分進行計算,總天數 = 20120101-20160101這一大塊時間 + 20160101-20160328這段時間 + 20110118-20111231這段時間,這種方法需要考慮的細節特別多,毫無意外我敗了……
后來想到的方法是用一個“標志時間”,比如說19700101,然后分別計算輸入的兩個日期距離這個“標志時間”的天數x和y,最后結果就是x-y(或者其他的關系,總值是兩個距離的差值,相信讀者能懂)。這個方法沒有去實現,因為我看了下面的方法,真是太神奇,太巧妙了!不過也僅僅是按照“實際情況”而已。
正如前一段的最后一句話,《算法筆記》中的方法僅僅是按照時間遞進的“實際情況”來進行模擬而已。(我想,這也是為什么晴神把日期問題放在第三章入門模擬的原因吧……)
什么叫做“按照時間遞進的實際情況”呢?就是從曉得日期time1一天一天的過到time2,也許說的不夠明白,下面請看代碼:
int sum = 1;//初始值為1而不是0while(year1<year2 || month1<month2 || day1<day2){ day1++; if(day1 > monthNum[month1][isLeapYear(year1)]){ month1++; day1 = 1;//注意點 } if(month1 > 12){ year1++; month1 = 1;//注意點 } sum++;}相信看到代碼就能夠理解上面的感悟了。
其中,isLeapYear()是一個判斷是否是閏年的函數bool isLeapYear(int year){ return ( (year%4 == 0 && year%100 != 0) || (year % 400 == 0) );}monthNum是一個數組int monthNum[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} };這個方法所有的巧妙之處幾乎都體現在上面這兩段代碼中,請讀者自行體會其中的精妙。
《算法筆記》購買地址。
新聞熱點
疑難解答