codeup 1928 日期差值

日期处理问题真是让人感到头疼,需要非常细心的去处理细节。(但是看了《算法笔记》上的解法,我觉得方法才是最重要的……)

下面以codeup上的一道题来介绍相关问题,以及从中习得的一些新的知识点。题目如下:

题目描述
有两个日期,求两个日期之间的天数,如果两个日期是连续的我们规定他们之间的天数为两天。

输入
有多组数据,每组数据有两行,分别表示两个日期,形式为YYYYMMDD

输出
每组数据输出一行,即日期差值

样例输入
20130101
20130105
样例输出
5

非常常规的一道题目,验证了“细节处理非常重要”这句话,但是更加验证了“方法更重要”这句话。

题目分析

首先,分析一下这个题目。我们需要做的事情有如下:

1. 处理日期的输入
2. 如何计算两个日期的差值

处理日期的输入

我的方法

我想到的是用之前刚刚学会的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而不是0

while(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} }; 

这个方法所有的巧妙之处几乎都体现在上面这两段代码中,请读者自行体会其中的精妙。

完整代码

#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) );
}

《算法笔记》购买地址

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值