王道机试第二章《经典入门》——2.日期类问题

前言

该例题考察了日期类问题中最基本的问题——求两个日期间的天数差,即求分别以两个特定日期为界的日期区间的长度。
解决这类区间问题有一个统一的思想——把原区间问题统一到起点确定的区间问题上去

例2.3 日期差值

在该例中,我们把问题统一到特定日期与一个原点时间(如0000年1月1日)的天数差。
当要求两个特定的日期之间的天数差时,我们只要将它们与原点日期的天数差相减,便能得到这两个特定日期之间的天数差(必要时加绝对值)。

题目描述

玖涯博客
玖涯博客

日期类问题有一个特别需要注意的要点——闰年,每逢闰年2月将会有29天,这对我们计算天数势必会产生重大的影响。
这里,我们必须明确闰年的判断规则——当年数不能被100整除时若其能被4整除则为闰年,或者其能被400整除时也是闰年。用逻辑语言表达出来即为Year % 100 != 0 && Year % 4 == 0 || Year % 400 == 0,当逻辑表达式为true时,其为闰年;反之则不是闰年。

我们提供一个技巧——预处理。
我们可以在程序真正开始处理输入数据之前,预处理出所有日期与原点日期之间的天数差并保存起来。当数据真正开始输入时,我们只需要用O(1)的时间复杂度将保存的数据读出,稍加处理便能得到答案。值得一提的是,预处理也是空间换时间的重要手段。

// 定义宏判断是否是闰年,方便计算每月天数
#define ISYEAP(x) x % 100 != 0 && x % 4 == 0 || x % 400 == 0 ? 1 : 0
//预存每月的天数,注意二月配合宏定义作特殊处理
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
};

struct Date { //日期类,方便日期的推移
int Day;
int Month;
int Year;
//计算下一天的日期
void nextDay() {
Day ++;
if (Day > dayOfMonth[Month][ ISYEAP(Year) ]) { //若日数超过了当月最大日数
Day = 1;
Month ++; //进入下一月
if (Month > 12) { //月数超过12
Month = 1;
Year ++; //进入下一年
}
}
}
};

完整C++代码

//
// Created by PM on 2020/2/1.
//
#include <stdio.h>
// 定义宏判断是否是闰年,方便计算每月天数
#define ISYEAP(x) x % 100 != 0 && x % 4 == 0 || x % 400 == 0 ? 1 : 0
//预存每月的天数,注意二月配合宏定义作特殊处理
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
};
//日期结构体,方便进行日期的推移计算
struct Date {
int Day;
int Month;
int Year;
//计算下一天的日期
void nextDay() {
Day ++;
//若日数超过了当月最大日数
if (Day > dayOfMonth[Month][ ISYEAP(Year) ]) {
Day = 1;
Month ++; //进入下一月
if (Month > 12) { //月数超过12
Month = 1;
Year ++; //进入下一年
}
}
}
};
int buf[5001][13][32]; //全局变量:保存预处理的天数
//求绝对值
int Abs(int x) {
return x < 0 ? -x : x;
}
int main () {
Date tmp;
int cnt = 0; //天数计数
tmp.Day = 1;
tmp.Month = 1;
tmp.Year = 0; //初始化日期类对象为0年1月1日
//默认日期不超过5000年
//O(n)=5000年日期天数
while(tmp.Year != 5001) {
//将该日与0年1月1日的天数差保存起来(使用了Hash的思想:将存储位置与数据本身对应起来的存储手段)
buf[tmp.Year][tmp.Month][tmp.Day] = cnt;
tmp.nextDay(); //计算下一天日期
cnt ++; //计数器累加,每经过一天计数器即+1,代表与原点日期的间隔又增加一天
}
int d1 , m1 , y1;
int d2 , m2 , y2;
while (scanf ("%4d%2d%2d",&y1,&m1,&d1) != EOF) {
scanf ("%4d%2d%2d",&y2,&m2,&d2); //读入要计算的两个日期
//用预处理的数据计算两日期差值,注意需对其求绝对值
printf("%d\n",Abs(buf[y2][m2][d2] - buf[y1][m1][d1]) + 1);
}
return 0;
}

代码中的注意点:

  1. 在保存某个特定日期与原点日期的天数差时,我们使用了三维数组,用年、月、日分别表示该数组下标,这便将日期本身与其存储地址联系了起来。
  2. 该例程的输入采用了某种技巧。因为题面规定用一个连续的八位数来代替日期,我们使用%4d来读取该八位数的前四位并赋值给代表年的变量,同理使用%2d%2d来读取其它后四位并两两赋值给月日。
  3. 我们将buf[5001][13][32]这个相对比较耗费内存的数组定义成全局变量。这是因为需要耗费大量的内存,若在main函数(其它函数也一样)之中定义该数组,其函数所可以使用的栈空间将不足以提供如此庞大的内存,出现栈溢出,导致程序异常终止。

我们也可以考虑一下,假如问题需要我们输出某个特定的日期,是那年的第几天,我们该怎样利用我们已经得到的数据来计算呢?
我们只需要用该日期与原点日期的天数减去那年元旦与原点日期便可得知该日期是当年的第几天。

例2.4 Day of week

题目描述

We now use the Gregorian style of dating in Russia. The leap years are years with number divisible by 4 but not divisible by 100, or divisible by 400. For example, years 2004, 2180 and 2400 are leap. Years 2004, 2181 and 2300 are not leap.
Your task is to write a program which will compute the day of week corresponding to a given date in the nearest past or in the future using today’s agreement about dating.

其大意为,输入一个日期,要求输出该日期为星期几。

解题思路

我们照样可以利用上例的思路来解答该题。
星期几是以七为周期循环的,那么我们只需要知道:

  1. 今天是星期几。
  2. 今天和所给定的那天相隔几天。

利用其对7求余数,我们便可以轻易的知道所给定的那天是星期几了。

C++代码


在该例中,预处理部分与上例保持一致。依旧是处理出每个日期与原点日期之间的天数间隔。
然后计算该日期天数差与当前日期天数差之间的差值,用7对其去模(即求余数),通过”星期数组”得到具体星期几。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值