-
有两个日期,求两个日期之间的天数,如果两个日期是连续的我们规定他们之间的天数为两天
原题地址:http://ac.jobdu.com/problem.php?pid=1096
题目描述:
-
输入:
-
有多组数据,每组数据有两行,分别表示两个日期,形式为YYYYMMDD
-
输出:
-
每组数据输出一行,即日期差值
-
样例输入:
-
20110412 20110422
-
样例输出:
-
11
-
来源:
- 闰年的判断规则:当年数能被4整除但不能被100整除时为闰年(如1900年为平年),或者年数能被400整除。定义宏表达式判断非常简洁明了。
- 计算某个日期与原点日期的差的做法如下:定义一个类DATE,不仅起到表示日期的作用,还能自动计算出下一天的具体日期(涉及月份间、年份间的递进),一方面不断计算出当前日期的下一个日期,另一方面累加计数器,计算当前日期与原点日期的偏移。
- 使用三维数组的下标表示年、月、日,即将日期本身与存储地址联系起来,提供了很方便的索引(有点Hash的感觉)
- 在%和d之间插入数字来读取特定位数的技巧:输入时用%4d来读取八位数的前四位年份,%2d%2d来读取月和日,比手动取字符串子串方便多了:)
- 耗费大量内存的数组或变量应定义在全局中,否则函数的栈空间将不足以提供大量内存空间,导致栈溢出StackOverflow,当然malloc也是可以的
之前没有接触过和日期有关的问题,第一次接触觉得还是比较有技巧的。
这道题考察的是日期类问题里最基本的问题——求两个给定日期间的天数差。第一反应就是判断一下这两个日期间有多少闰年、多少月份之类的办法,但是显然实现起来并不容易而且很可能出错,需要有一个统一的策略,书上提到“解决这类区间问题有一个统一的思想——把原区间问题统一到起点确定的区间问题上去”,因此提出了一种把特定日期间的天数差,统一到这两个日期与一个原点时间(如0000年1月1日)的天数差,即X1-X2转换成了(X1-X0) - (X2-X0),当然必要时还要加绝对值,以及区间开闭是否加1。
通过这样做还有个好处就是利用了预处理,在程序真正测试输入数据之前,预处理出范围内每个日期与原点的天数差并保存在数组中,之后的每组测试数据就只需要O(1)复杂度,将这两个时间的偏移读出来作差、取绝对值。预处理是空间换时间的重要手段(“保存预处理所得到数据所需的内存来换取实时处理所需要的耗时”)。
值得注意的几个点:
这类题目的变题可以有很多,比如给定日期在那一年中是第几天,某个日期是星期几(九度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)