题解 A94674日历制作
2026-05-24 10:30:36
发布于:广东
题目:打印月历
已知今年是平年(2月有28天),并且9月1日是星期一。
输入一个整数 m(1 ≤ m ≤ 12),表示月份。
请你按照下面的格式,打印出该月份的月历。
月历格式要求:
- 第一行输出星期缩写:
MON TUE WED THU FRI SAT SUN(每个缩写之间用一个空格隔开,注意最后没有多余空格) - 第二行开始,按日期顺序输出该月的每一天。
- 日期输出占 3 个字符宽度(例如
1、10、31),右对齐。 - 每个日期之间用一个空格隔开,但是每行末尾不能有多余空格。
- 每周的 星期一 必须对齐到第一列(即
MON下方)。 - 当一周结束(星期日之后)或者该月最后一天打印完毕后,需要换行。
解题思路
这个题目的关键是算出某个月的第一天是星期几。
已知9月1日是星期一,那么其他月份的第一天可以通过计算与9月的天数差来得到。
1. 月份天数表
我们用一个数组 days[13] 存储每个月的天数,其中 days[0] 不用,下标112对应1月12月:
days[1]=31, days[2]=28, days[3]=31, days[4]=30, days[5]=31, days[6]=30,
days[7]=31, days[8]=31, days[9]=30, days[10]=31, days[11]=30, days[12]=31
2. 计算某月第一天是星期几
设 w 表示星期几,其中 1 代表星期一,2 代表星期二,……,7 代表星期日。
-
已知9月1日:
w = 1(星期一)。 -
对于 m > 9(10月、11月、12月):
从9月开始,累加9月、10月……直到 m-1 月的天数,每过一天星期数加1,但要模7。
例如10月1日 = 9月1日 + 9月的天数(30天) → 星期 = (1 + 30) mod 7 = 31 mod 7 = 3?但是注意:9月1日星期一,9月30日是星期二?我们来算:9月1日星期一,那么9月30日是?从1到30过了29天,29 mod 7 = 1,所以9月30日是星期一+1=星期二?不对,应该是9月1日星期一,9月2日星期二,……9月30日是星期几?1号周一,8号周一,15号周一,22号周一,29号周一,30号周二。所以从9月1日到10月1日过了30天,30 mod 7 = 2,星期一加2天是星期三。代码中公式是w = (w + days[i] - 1) % 7 + 1。为什么减1?因为从当前月的1号到下个月的1号,相差的是当前月的天数。比如9月有30天,9月1日到10月1日经过30天,星期变化是(1 + 30) % 7得到3?但上面计算结果是星期三对应3?星期一1,加30天得31,31%7=3,确实是星期三。但是代码中用了(w + days[i] - 1) % 7 + 1,如果w=1,days[i]=30,则 (1+29)%7+1 = 30%7+1=2+1=3,正确。所以这个公式等价于先减1再加1处理。 -
对于 m < 9(1月~8月):
需要往前推。例如8月1日,从9月1日往回推8月的天数?注意8月有31天,9月1日往前31天是8月1日?实际上8月1日到9月1日经过31天,所以8月1日的星期 = 9月1日 - 31天。星期数减31 mod 7。代码中用循环从8月向下到m,每次减当前月的天数,然后调整到1~7范围。
公式:w = ((w - days[i]) % 7 + 7) % 7;如果得到0则改为7。这是经典的取模处理。
3. 打印日历
- 先打印表头。
- 算出当月天数
d = days[m]和当月1号的星期w。 - 输出空格使1号对齐到正确的星期列:由于每个日期输出占3格,但表头每个缩写占3格?实际上代码中使用
printf(" ");输出一个空格来对齐,每个空格占1列,而日期数字用%3d输出(占3列)。为了能让数字刚好在对应缩写下,需要先输出w-1个空格(因为星期一不需要空格)。但注意数字宽度3,表头每个缩写也是3个字符,但它们之间还有一个空格?所以需要仔细分析,不过我们直接按照代码的逻辑:for (int i=1; i<w; i++) cout<<" ";这样就能保证1号出现在正确位置(因为printf("%3d",1) 会先输出两个空格再输出1,整体占3列,而前面的空格正好填补到对应列)。 - 然后循环输出1到d,每输出一个数字后,更新星期
w = w % 7 + 1(星期循环)。 - 如果
w == 1(意味着刚输完星期日,下一天是星期一,需要换行)或者i == d(最后一天),则换行;否则输出一个空格分隔。
参考代码(就是题目给出的代码)
#include<bits/stdc++.h>
using namespace std;
int days[20] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
int main() {
int m;
cin>>m;
cout<<"MON TUE WED THU FRI SAT SUN\n";
int d = days[m];
int w = 1; // 9月1日是星期一
// 计算 m 月 1 日是星期几
if (m > 9) {
for (int i = 9; i < m; i++)
w = (w + days[i] - 1) % 7 + 1;
} else if (m < 9) {
for (int i = 8; i >= m; i--) {
w = ((w - days[i]) % 7 + 7) % 7;
if (w == 0) w = 7;
}
}
// 打印空格对齐
for (int i = 1; i < w; i++)cout<<" ";
// 打印日期
for (int i = 1; i <= d; i++) {
printf("%3d", i);
w = w % 7 + 1;
if (w == 1 || i == d)cout<<endl;
else cout<<" ";
}
}
代码解释
days数组:记录每个月的天数,2月是28天(平年)。scanf读入月份m。- 先打印星期表头。
d = days[m]得到这个月的天数。w = 1表示9月1日是星期一。- 计算月份 m 的第一天是星期几:
- 如果 m > 9,从9月到 m-1 月,每个月累加天数,更新 w。
- 如果 m < 9,从8月倒着减到 m 月,每次减去该月的天数,并调整到 1~7。
- 如果 m == 9,w 保持为 1。
- 打印空格:因为星期一不需要前导空格,所以只打印
w-1个空格。 - 循环打印日期:
printf("%3d", i)使数字占3格右对齐。每打印一个数字,星期数加1(循环1~7)。如果星期变成1(即过了星期日)或者到了月底,就换行,否则打印一个空格。
注意事项
- 这个题目假设平年且9月1日是星期一,不需要考虑闰年。
- 打印格式要严格,数字占3格,星期表头之间一个空格,日期之间一个空格。
- 最后一天之后不能有多余空格,要直接换行。
如果这份题解对你有帮助的话,please thumb up!
这里空空如也





有帮助,赞一个