九度OJ 1096 日期差值

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

题目描述:

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

输入:

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

输出:

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

样例输入:
20110412
20110422
样例输出:
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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值