顺推每年元旦的星期,利用月初星期和模 7 公式定位第几个指定星期。
OJ: shumeng
题目 ID: CSP201503C
难度:入门
标签:模拟日期
日期: 2026-07-31 16:21
形式化题目
已知 yyyy/mm/dd 格式输出;若该年 none。星期编号
思路
先看一个完全按题意逐日推进的基准程序,它从
cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-07-31 16:21
* update_at: 2026-08-17 22:51
*/
// brute.cpp:逐日模拟从 1850 年开始的日历。
#include <bits/stdc++.h>
using namespace std;
bool is_leap(int year) {
return year % 400 == 0 || (year % 4 == 0 && year % 100 != 0);
}
int days_in_month(int year, int month) {
int days[13] = {0, 31, 28, 31, 30, 31, 30,
31, 31, 30, 31, 30, 31};
if (month == 2 && is_leap(year)) return 29;
return days[month];
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int target_month, kth, target_weekday, first_year, last_year;
cin >> target_month >> kth >> target_weekday >> first_year >> last_year;
vector<int> answer(last_year - first_year + 1, 0);
int weekday = 2;
for (int year = 1850; year <= last_year; year++) {
int seen = 0;
for (int month = 1; month <= 12; month++) {
int limit = days_in_month(year, month);
for (int day = 1; day <= limit; day++) {
if (month == target_month && weekday == target_weekday) {
seen++;
if (seen == kth && year >= first_year) {
answer[year - first_year] = day;
}
}
weekday = weekday % 7 + 1;
}
}
}
for (int year = first_year; year <= last_year; year++) {
int day = answer[year - first_year];
if (day == 0) {
cout << "none\n";
} else {
cout << year << '/' << setw(2) << setfill('0') << target_month << '/'
<< setw(2) << day << setfill(' ') << '\n';
}
}
return 0;
}brute.cpp 逐日模拟最忠实于题意,适合作为对拍基准,但最多要推进到
核心公式
若目标月的 1 日是星期 first,则:
- 该月第一个星期
c的日期是; - 第
个星期 c的日期是第一个的日期再加; - 若算出的日期超过该月天数,说明该月没有第
个星期 c,输出none。
推算每月 1 日的星期
已知
代码
cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-07-31 16:21
* update_at: 2026-08-17 22:51
*/
#include <bits/stdc++.h>
using namespace std;
// 判断 year 是否为闰年:400 的倍数,或 4 的倍数但不是 100 的倍数。
bool is_leap(int year) {
return year % 400 == 0 || (year % 4 == 0 && year % 100 != 0);
}
// 返回 year 年 month 月的天数,2 月需要根据闰年特殊处理。
int days_in_month(int year, int month) {
int days[13] = {0, 31, 28, 31, 30, 31, 30,
31, 31, 30, 31, 30, 31};
if (month == 2 && is_leap(year)) return 29;
return days[month];
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int month, kth, weekday_needed, first_year, last_year;
cin >> month >> kth >> weekday_needed >> first_year >> last_year;
// 1 表示星期一,1850 年 1 月 1 日是星期二。
// first_weekday_of_year 记录当前年份 1 月 1 日是星期几。
int first_weekday_of_year = 2;
for (int year = 1850; year <= last_year; year++) {
// 从元旦星期顺推目标月 1 日是星期几。
int first_weekday_of_month = first_weekday_of_year;
for (int current_month = 1; current_month < month; current_month++) {
first_weekday_of_month += days_in_month(year, current_month);
first_weekday_of_month = (first_weekday_of_month - 1) % 7 + 1;
}
if (year >= first_year) {
// 目标月 1 日是 first 时,第一个星期 c 的日期是 1+(c-first+7)%7。
int first_day = 1 + (weekday_needed - first_weekday_of_month + 7) % 7;
int answer_day = first_day + 7 * (kth - 1);
if (answer_day > days_in_month(year, month)) {
cout << "none\n";
} else {
cout << year << '/' << setw(2) << setfill('0') << month << '/'
<< setw(2) << answer_day << setfill(' ') << '\n';
}
}
// 用当年天数推进到下一年的元旦星期。
first_weekday_of_year += is_leap(year) ? 366 : 365;
first_weekday_of_year = (first_weekday_of_year - 1) % 7 + 1;
}
return 0;
}复杂度
- 时间:对每个年份只累加
个月,设年份数为 ,总时间为 。 - 空间:只使用常量空间,
。
总结
日期题先固定星期的编号方式,再用“月初星期 + 模 7 偏移”公式定位目标日期。闰年规则只影响每月天数和下一年元旦的星期,把它封装成 is_leap 和 days_in_month 两个函数即可避免重复判断。