题目:打印月历
已知今年是平年(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月:
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(最后一天),则换行;否则输出一个空格分隔。
参考代码(就是题目给出的代码)
代码解释
* 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!