LuoguP1202 [USACO1.1]黑色星期五Friday the Thirteenth 题解

真·模拟题,注意细节。


细节是什么?能吃吗?

首先最主要的还是处理闰年这个问题,我们先回去看题目,其中的第3、4个你要知道的就很好地讲明了处理闰年的规则:

年份可以被\(4\)整除的为闰年(\(1992=4*498\) 所以 \(1992\)年是闰年,但是\(1990\)年不是闰年)。

以上规则不适合于世纪年(即能被\(100\)整除的年份——作者注)。可以被400整除的世纪年为闰年,否则为平年。所以,\(1700,1800,1900\)\(2100\)年是平年,而\(2000\)年是闰年。

所以我们可以想出这样一个判断闰年的过程:

\(Step~1:\)判断是否是世纪年。如果是的话,判断是否能被\(400\)整除,是则该年份是闰年,否则不是。

\(Step~2:\)否则,判断完该年份不是世纪年之后,只需要看其是否能被\(4\)整除,是则该年份是闰年,否则不是。

至此,如下的年份判断程序就很清晰明了了:

bool check(int n) {
	if(n % 100 == 0) {
		if(n % 400 == 0)	return 1;
		return 0;
	}
	if(n % 4 == 0)	return 1;
	return 0;
}

这段打完之后,另外的问题又扑面而来:如何转换日期呢?

首先,我们知道这道题目给我们的初始日期为\(1900\)\(1\)\(1\)日星期一。其次,我们再回看题目:

\(2,4,6,11\)\(9\)月有30天.其他月份除了\(2\)月都有\(31\)天.闰年\(2\)月有\(29\)天,平年\(2\)月有\(28\)天。

我们都知道,从本月末到下月初,月份要加\(1\),天份要归\(1\)。从今年末到明年初,年份要加\(1\),月份和天份都归\(1\)。星期数到了\(7\),下一天也要归\(1\)

所以,我们想到了如下的转换日期程序:

\(Step~1:\)\(1900\)\(1\)\(1\)日星期一开始!

\(Step~2:\)如果一切正常,天份加\(1\),星期数也加\(1\)

\(Step~3:\)如果到了星期末,就要将星期数及时归\(1\)

\(Step~4:\)如果到了月末,月份就要加\(1\),天份同时要归\(1\)

\(Step~5:\)在执行\(Step~4\)的时候,如果还到了年末,年份加\(1\),月份和天份同时归\(1\)

程序也很明了了吧,自己先打代码,若有困难再看以下代码:

while(y <= 1900 + n - 1) {
//		printf("%d/%d/%d %d\n", y, m, d, week);
//		y是年份,m是月份,d是天份,week是星期数
//		ans[i]指对应星期数13日出现的次数
		if(d == 13) ans[week]++;
		week++;
		if(week > 7)	week = 1;
		d++;
		if(d > (m == 2 ? (check(y) ? f[m] + 1 : f[m]) : f[m])) {
			m++;
			d = 1;
		}
		if(m > 12) {
			y++;
			m = 1;
		}	
	}

输出就没什么好讲的,可以暴力输出,可以用两个循环输出,还可以用各种奇葩的方法输出。反正随你们便。

下面贴完整代码:

#include <cstdio>
#include <algorithm>
using namespace std;

inline int read() {
	int f = 1, x = 0;
	char c = getchar();
	while(c < '0' || c > '9') {
		if(c == '-') f = -1;
		c = getchar();
	}
	while(c >= '0' && c <= '9') {
		x = x * 10 + c - '0';
		c = getchar();
	}
	return x * f;
}
const int f[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
int y = 1900, m = 1, d = 1, n, week = 1;
int ans[7];

bool check(int n) {
	if(n % 100 == 0) {
		if(n % 400 == 0)	return 1;
		return 0;
	}
	if(n % 4 == 0)	return 1;
	return 0;
}
int main() {
	n = read();
	while(y <= 1900 + n - 1) {
//		printf("%d/%d/%d %d\n", y, m, d, week);
		if(d == 13) ans[week]++;
		week++;
		if(week > 7)	week = 1;
		d++;
		if(d > (m == 2 ? (check(y) ? f[m] + 1 : f[m]) : f[m])) {
			m++;
			d = 1;
		}
		if(m > 12) {
			y++;
			m = 1;
		}	
	}
	for(int i = 6; i <= 7; ++i)
		printf("%d ", ans[i]);
	for(int i = 1; i <= 5; ++i)
		printf("%d ", ans[i]);
	return 0;
}
posted @ 2021-12-23 21:13  Eason_AC  阅读(101)  评论(0)    收藏  举报