处理日期问题之前首先明确平年闰年
公元年数可被4整除为闰年,但是正百的年数必须是可以被400整除的才是闰年。其他都是平年。闰年的2月有29天。闰年366天 平年365天
题目描述
有两个日期,求两个日期之间的天数,如果两个日期是连续的我们规定他们之间的天数为两天。
输入
有多组数据,每组数据有两行,分别表示两个日期,形式为YYYYMMDD
输出
每组数据输出一行,即日期差值
样例输入
20130101
20130105
样例输出
5
非常常规的一道题目,验证了“细节处理非常重要”这句话,但是更加验证了“方法更重要”这句话。
首先,分析一下这个题目。我们需要做的事情有如下:
1. 处理日期的输入
2. 如何计算两个日期的差值
处理日期的输入
我的方法
我想到的是用之前刚刚学会的sscanf 来处理输入,如下:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
这种方法乍一看是可以的,不过事实上也的确可以。我们获取输入的目的无非就是想要把年、月、日拿出来,而这种方法已经达到了这样的一个目的。
不过为了而后面的程序,我们必须把time1和time2分出个大小来,而这种拿法刚开始我认为在比较time1和time2大小上非常麻烦,需要有很多的分支判断。再写这篇文章的时候才猛然醒悟,类比后面的方法,此处的比较大下可以直接用串比较函数(strcmp函数)进行比较即可,亲测可行。
《算法笔记》处理输入的方法
下面给出《算法笔记》上处理输入的方法,自己没想到上面所说的字符串比较方法之前,觉得这个方法真实无比的巧妙啊!代码如下:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
从代码可以看出,直接用两个整形变量拿到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,也许说的不够明白,下面请看代码:
相信看到代码就能够理解上面的感悟了。
其中,isLeapYear()是一个判断是否是闰年的函数
- 1
- 2
- 3
monthNum是一个数组
- 1
这个方法所有的巧妙之处几乎都体现在上面这两段代码中,请读者自行体会其中的精妙。
完整代码
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
#include<stdio.h>
bool isLeapYear(int year);
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} };
int main(){
int time1, time2; //time1存小时间,time2存大时间
int year1,month1,day1, year2,month2,day2;
while(scanf("%d%d",&time1, &time2) != EOF) {
if(time1 > time2){
int temp = time1;
time1 = time2;
time2 = temp;
}
// 除法(10^(x+1))拿到前面的x位数,模拿到后面x位数
year1 = time1/10000; month1 = time1%10000/100; day1 = time1%100;
year2 = time2/10000; month2 = time2%10000/100; day2 = time2%100;
int sum = 1;
while(year1<year2 || month1<month2 || day1<day2){
day1++;
if(day1 > monthNum[month1][isLeapYear(year1)]){
month1++;
day1 = 1;
}
if(month1 > 12){
year1++;
month1 = 1;
}
sum++;
}
printf("%d\n",sum);
}
return 0;
}
bool isLeapYear(int year){
return ( (year%4 == 0 && year%100 != 0) || (year % 400 == 0) );
}