@TOC

日期类问题

1. 日期差值

有两个日期,计算两个日期之间的天数,如果两个日期是连续的,我们规定他们之间的天数为两天。
输入:
    有多组输入,每组数据有两行,分别表示两个日期,形式为YYYYMMDD;
输出:
    每组数据输出一行,即日期差值。
样例输入:
    20110412
    20110422
样例输出:
    11

解析:将原问题统一到一个确定的起点区间上去,比如0年1月1日,当我们输入日期的时候计算出日期与起点区间的差,当我们计算两个日期的差时只需要将它们与起点区间的差相减就OK了,这个做法可以先计算出每个日期与起点日期之间的差,当我们输入日期的时候在O(1)的时间内可以查出当前日期与起点日期的差,相当于是用空间换取时间的做法。

注意:
闰年的判断条件是:当年数如果不能被100整除但是能被4整除时为闰年或者当年数能被40整除时为闰年

实现的C++程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include <stdio.h>
#include <iostream>

#define ISLEAPYEAR(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 year;
int month;
int day;

void NextDay() {
// 计算下一天的日期
day++;
if (day > DayofMonth[month][ISLEAPYEAR(year)]) {
day = 1;
month++;
if (month > 12) {
month = 1;
year++;
}
}
}

};

int buf[5001][13][32]; //保存预处理的天数
int abs(int x) {
return x < 0 ? -x : x;
}

int main() {
Date tmp;
tmp.year = 0;
tmp.month = 1;
tmp.day = 1;
int count = 0; // 天数计数
while(tmp.year != 5001) {
buf[tmp.year][tmp.month][tmp.day] = count;
count++;
tmp.NextDay();
}

int y1, m1, d1;
int y2, m2, d2;

while(scanf("%4d%2d%2d", &y1, &m1, &d1) != EOF) {
scanf("%4d%2d%2d", &y2, &m2, &d2);
printf("%d\n", abs(buf[y1][m1][d1] - buf[y2][m2][d2]) + 1);
}
return 0;
}

以上代码简析:

(1)将数据本身与数据的存储地址联系起来,存储日期的时候使用三维数组,这样存取方便;(2)程序输入采取了技巧,因为题目规定使用连续的8位数据,所以使用%4d来读取前四位并赋值给year,后面的%2d%2d也是同样的思路;(3)在main函数体外定义预处理数组buf[5001][13][32],避免函数在运行的时候栈的空间不够而出现栈溢出的情况。

2. Day of Week

给出一个日期,求出这个时期是星期几。
输入样例:                      输出样例:
    9  October 2001                 Tuesday
    14 October 2001                 Sunday

解析:这个问题也同样可以使用预处理的思路来解决,首先需要知道今天星期几,给定日期与今天相比差几天,利用这个数字对7求模,即可得出给定日期星期几。

(1)C++实现程序如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#include <stdio.h>
#include <iostream>
#include <string.h>
using namespace std;

#define ISLEAPYEAR(x) (x % 100 != 0 && x % 4 == 0) || x % 400 == 0 ? 1 : 0

char 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 year;
int month;
int day;

void NextDay() {
day++;
if (day > DayofMonth[month][ISLEAPYEAR(year)]) {
day = 1;
month++;
if (month > 12) {
month = 1;
year++;
}
}
}

};

char MonthName[13][20] = {
"",
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December"
};

char WeekName[7][20] = {
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday"
};

int buff[5001][13][32];

int main() {
Date tmp;
tmp.year = 0;
tmp.month = 1;
tmp.day = 1;
int count = 1;
while(tmp.year != 5001) {
// 预处理天数
buff[tmp.year][tmp.month][tmp.day] = count;
tmp.NextDay();
count++;
}

int year;
char month[20];
int day;
while(scanf("%d%s%d", &day, month, &year) != EOF) {
int i = 0;
while(strcmp(month, MonthName[i]) != 0)
i++;

// for (i = 0; i < 13 && month != MonthName[i]; i++);
// 字符数组不可以这样比较,month是month[0]的地址,
// MonthName[i]是MonthName[i][0]的地址

// 今天的日期是2018那年12月27日,星期四
int days = buff[year][i][day] - buff[2018][12][27];
days += 4; // 因为今天是周四,所以需要加上这个日期
// 对日期取模,并且为保证日期下标为正,这里必须加7
puts(WeekName[(days % 7 + 7) % 7]);
}
return 0;
}

(2)Zeller公式求解

历史上的某一天是星期几?未来的某一天是星期几?关于这个问题,有很多计算公式(两个通用计算公式和一些分段计算公式),其中最著名的是蔡勒(Zeller)公式。即用公式:
        w=y+[y/4]+[c/4]-2c+[26(m+1)/10]+d-1
公式中的符号含义如下,w:星期;c:世纪-1;y:年(后两位数);m:月(m大于等于3,小于等于14,即在蔡勒公式中,某年的1、2月要看作上一年的13、14月来计算,比如2003年1月1日要看作2002年的13月1日来计算);d:日;[ ]代表取整,即只要整数部分。(C是世纪数减一,y是年份后两位,M是月份,d是日数。1月和2月要按上一年的13月和 14月来算,这时C和y均按上一年取值。)算出来的W除以7,余数是几就是星期几。如果余数是0,则为星期日。
以2049年10月1日(100周年国庆)为例,用蔡勒(Zeller)公式进行计算,过程如下:
蔡勒(Zeller)公式:
    w=y+[y/4]+[c/4]-2c+[26(m+1)/10]+d-1
    =49+[49/4]+[20/4]-2×20+[26× (10+1)/10]+1-1
    =49+[12.25]+5-40+[28.6]
    =49+12+5-40+28
    =54 (除以7余5)即2049年10月1日(100周年国庆)是星期5。

C++++实现如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <stdio.h>
#include <iostream>
#include <string.h>
using namespace std;

char MonthName[13][20] = {
"",
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December"
};

char WeekName[7][20] = {
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday"
};

int main() {

int preyear;
int postyear;
char month[20];
int day;
while(scanf("%d%s%2d%2d", &day, month, &preyear, &postyear) != EOF) {
int i = 0;
while(strcmp(month, MonthName[i]) != 0)
i++;

if (i == 1 || i == 2)
i += 12;

// 蔡勒公式: w=y+[y/4]+[c/4]-2c+[26(m+1)/10]+d-1
int weeks = postyear + postyear / 4 + (preyear / 4) -
2 * preyear + (26 * (i + 1) / 10) + day - 1;
puts(WeekName[weeks % 7]);
}
return 0;
}