九度OJ 1096 日期差值

来源:互联网 发布:淘宝店怎么看分 编辑:程序博客网 时间:2024/04/29 22:31

原题地址:http://ac.jobdu.com/problem.php?pid=1096

题目描述:

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

输入:

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

输出:

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

样例输入:
2011041220110422
样例输出:
11
来源:

2009年上海交通大学计算机研究生机试真题


之前没有接触过和日期有关的问题,第一次接触觉得还是比较有技巧的。

这道题考察的是日期类问题里最基本的问题——求两个给定日期间的天数差。第一反应就是判断一下这两个日期间有多少闰年、多少月份之类的办法,但是显然实现起来并不容易而且很可能出错,需要有一个统一的策略,书上提到“解决这类区间问题有一个统一的思想——把原区间问题统一到起点确定的区间问题上去”,因此提出了一种把特定日期间的天数差,统一到这两个日期与一个原点时间(如0000年1月1日)的天数差,即X1-X2转换成了(X1-X0) - (X2-X0),当然必要时还要加绝对值,以及区间开闭是否加1。

通过这样做还有个好处就是利用了预处理,在程序真正测试输入数据之前,预处理出范围内每个日期与原点的天数差并保存在数组中,之后的每组测试数据就只需要O(1)复杂度,将这两个时间的偏移读出来作差、取绝对值。预处理是空间换时间的重要手段(“保存预处理所得到数据所需的内存来换取实时处理所需要的耗时”)。

值得注意的几个点:

  1. 闰年的判断规则:当年数能被4整除但不能被100整除时为闰年(如1900年为平年),或者年数能被400整除。定义宏表达式判断非常简洁明了。
  2. 计算某个日期与原点日期的差的做法如下:定义一个类DATE,不仅起到表示日期的作用,还能自动计算出下一天的具体日期(涉及月份间、年份间的递进),一方面不断计算出当前日期的下一个日期,另一方面累加计数器,计算当前日期与原点日期的偏移。
  3. 使用三维数组的下标表示年、月、日,即将日期本身与存储地址联系起来,提供了很方便的索引(有点Hash的感觉)
  4. %和d之间插入数字来读取特定位数的技巧:输入时用%4d来读取八位数的前四位年份,%2d%2d来读取月和日,比手动取字符串子串方便多了:)
  5. 耗费大量内存的数组或变量应定义在全局中,否则函数的栈空间将不足以提供大量内存空间,导致栈溢出StackOverflow,当然malloc也是可以的
这类题目的变题可以有很多,比如给定日期在那一年中是第几天,某个日期是星期几(九度OJ 1043 Day of Week)等等,思路都是一样滴。

AC代码如下:
#include <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cmath>using namespace std;#define MAX_YEAR 5001#define IsLeapYear(x) ((x%100!=0 && x%4==0)||x%400==0)?1:0 //判断x年是不是闰年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};typedef struct date //定义日期结构体,用于之后的预处理{    int year,month,day;    void next_day() //当前日期的下一天    {        day++;        if(day > dayOfMonth[month][IsLeapYear(year)]) //超过了当年当月的最大天数        {            day = 1;            month++;            if(month > 12) //一年过完            {                month = 1;                year++;            }        }    }}DATE;int buf[MAX_YEAR][13][32]; //存放MAX_YEAR年中的所有日期与基准日期0000年1月1日的间隔void PreProcess() //采用预处理,用空间换时间,提前处理好,读取时只用O(1)复杂度{    DATE tmp;    tmp.year = 0; tmp.month = 1; tmp.day = 1;    int interval = 0; //记录每个日期与基准日期的日期差    while(tmp.year != MAX_YEAR)    {        buf[tmp.year][tmp.month][tmp.day] = interval;        tmp.next_day();        ++interval;    }    return;}int main(){    PreProcess();    int y1, m1, d1;    int y2, m2, d2;    while(~scanf("%4d%2d%2d", &y1, &m1, &d1)) //注意scanf里%d中的数字表示截取多少位,实现了直接分解为三个int    {        scanf("%4d%2d%2d", &y2, &m2, &d2);        cout << abs(buf[y1][m1][d1]-buf[y2][m2][d2])+1 << endl; //注意绝对值和+1    }    return 0;}

内存占用:9644Kb 耗时:30ms(预处理的效果明显?)

算法复杂度:取决预处理计算日期的复杂度:O(n)

0 0
原创粉丝点击