202509-日历制作(luogu-B4414)

202509-日历制作(luogu-B4414)

GESP C++ 2025年9月三级真题,一维数组考点,难度★★☆☆☆。

luogu-B4414 [GESP202509 三级] 日历制作

题目要求

题目描述

小 A 想制作 20252025 年每个月的日历。他希望你能编写一个程序,按照格式输出给定月份的日历。

具体来说,第一行需要输出 MON TUE WED THU FRI SAT SUN,分别表示星期一到星期日。接下来若干行中依次输出这个月所包含的日期,日期的个位需要和对应星期几的缩写最后一个字母对齐。例如,202520259911 日是星期一,在输出九月的日历时,11 号的个位 11 就需要与星期一 MON 的最后一个字母 N 对齐。九月的日历输出效果如下:

MON TUE WED THU FRI SAT SUN
 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

你能帮助小 A 完成日历的制作吗?

输入格式

一行,一个正整数 mm,表示需要按照格式输出 20252025mm 月的日历。

输出格式

输出包含若干行,表示 20252025mm 月的日历。

输入输出样例 #1

输入 #1
9
输出 #1
MON TUE WED THU FRI SAT SUN
  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

输入输出样例 #2

输入 #2
6
输出 #2
MON TUE WED THU FRI SAT SUN
                          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

说明/提示

对于所有测试点,保证 1m121 \leq m \leq 12


题目分析

  1. 确定 2025 年任意月份 1 号是星期几
    以 2025-09-01(星期一)为基准,向前或向后累加整月天数,再对 7 取模即可得到目标月首日的星期偏移(1=MON…7=SUN)。

  2. 排版日历

    • 第一行固定输出星期标题。
    • 根据“首日是星期几”先在对应位置补空格,使 1 号的个位与星期缩写的末字符对齐。
    • 之后顺序输出该月所有日期,每 7 个换行。
  3. 实现策略

    • 方法一(硬编码):把 12 个月的首日偏移和天数全部手算后写进 switch,考场“秒过”。
    • 方法二(计算法):用数组预存每月天数,以 9 月为基准向前/向后累加天数,动态算出偏移,通用且易改年份。

两种思路均只需 O(1)O(1) 预处理、O(D)O(D) 输出(DD 为当月天数),轻松通过 GESP 三级数据范围。


示例代码

方法一 应试”白给“法

#include <cstdio>
#include <iostream>

int main() {
    int m;
    std::cin >> m;                          // 读入需要输出的月份
    std::cout << "MON TUE WED THU FRI SAT SUN\n";  // 打印星期标题行

    // 针对2025年各月1日对应的星期偏移(已硬编码)
    // 通过 switch 分支直接控制该月日历的排版
    switch (m) {
        case 1:
            // 1月:1号为星期三,前面空2格
            for (int i = 1; i <= 33; i++) { // 33 = 31天 + 2个空位
                if (i <= 2) {
                    printf("    ");         // 占位4空格(含后续分隔)
                } else {
                    printf("% 3d ", i - 2); // 右对齐3位数字 + 1空格
                }
                if (i % 7 == 0) {           // 每7列换行
                    std::cout << "\n";
                }
            }
            break;
        case 2:
            // 2月:1号为星期六,前面空5格
            for (int i = 1; i <= 33; i++) { // 28天 + 5空位
                if (i <= 5) {
                    printf("    ");
                } else {
                    printf("% 3d ", i - 5);
                }
                if (i % 7 == 0) {
                    std::cout << "\n";
                }
            }
            break;
        case 3:
            // 3月:1号为星期六,前面空5格
            for (int i = 1; i <= 36; i++) { // 31天 + 5空位
                if (i <= 5) {
                    printf("    ");
                } else {
                    printf("% 3d ", i - 5);
                }
                if (i % 7 == 0) {
                    std::cout << "\n";
                }
            }
            break;
        case 4:
            // 4月:1号为星期二,前面空1格
            for (int i = 1; i <= 31; i++) { // 30天 + 1空位
                if (i <= 1) {
                    printf("    ");
                } else {
                    printf("% 3d ", i - 1);
                }
                if (i % 7 == 0) {
                    std::cout << "\n";
                }
            }
            break;
        case 5:
            // 5月:1号为星期四,前面空3格
            for (int i = 1; i <= 34; i++) { // 31天 + 3空位
                if (i <= 3) {
                    printf("    ");
                } else {
                    printf("% 3d ", i - 3);
                }
                if (i % 7 == 0) {
                    std::cout << "\n";
                }
            }
            break;
        case 6:
            // 6月:1号为星期日,前面空6格
            for (int i = 1; i <= 36; i++) { // 30天 + 6空位
                if (i <= 6) {
                    printf("    ");
                } else {
                    printf("% 3d ", i - 6);
                }
                if (i % 7 == 0) {
                    std::cout << "\n";
                }
            }
            break;
        case 7:
            // 7月:1号为星期二,前面空1格
            for (int i = 1; i <= 32; i++) { // 31天 + 1空位
                if (i <= 1) {
                    printf("    ");
                } else {
                    printf("% 3d ", i - 1);
                }
                if (i % 7 == 0) {
                    std::cout << "\n";
                }
            }
            break;
        case 8:
            // 8月:1号为星期五,前面空4格
            for (int i = 1; i <= 35; i++) { // 31天 + 4空位
                if (i <= 4) {
                    printf("    ");
                } else {
                    printf("% 3d ", i - 4);
                }
                if (i % 7 == 0) {
                    std::cout << "\n";
                }
            }
            break;
        case 9:
            // 9月:1号为星期一,无偏移
            for (int i = 1; i <= 30; i++) { // 30天
                printf("% 3d ", i);
                if (i % 7 == 0) {
                    std::cout << "\n";
                }
            }
            break;
        case 10:
            // 10月:1号为星期三,前面空2格
            for (int i = 1; i <= 33; i++) { // 31天 + 2空位
                if (i <= 2) {
                    printf("    ");
                } else {
                    printf("% 3d ", i - 2);
                }
                if (i % 7 == 0) {
                    std::cout << "\n";
                }
            }
            break;
        case 11:
            // 11月:1号为星期六,前面空5格
            for (int i = 1; i <= 35; i++) { // 30天 + 5空位
                if (i <= 5) {
                    printf("    ");
                } else {
                    printf("% 3d ", i - 5);
                }
                if (i % 7 == 0) {
                    std::cout << "\n";
                }
            }
            break;
        case 12:
            // 12月:1号为星期一,无偏移
            for (int i = 1; i <= 31; i++) { // 31天
                printf("% 3d ", i);
                if (i % 7 == 0) {
                    std::cout << "\n";
                }
            }
            break;
        default:
            break;
    }
    return 0;
}

方法二 正常计算法

#include <cstdio>
#include <iostream>

// 2025年各月天数,0号元素占位,1~12月分别对应实际天数
int month_days[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

int main() {
    int m;
    std::cin >> m;                          // 读入需要输出的月份
    std::cout << "MON TUE WED THU FRI SAT SUN\n";  // 打印星期标题行

    int start_day = 1;  // 2025年9月1日为星期一,作为基准
    int total_days = 0; // 累计从基准月到目标月之间的总天数

    if (m == 9) {
        // 目标月就是9月,无需累加,直接已知start_day=1
        start_day = 1;
    } else if (m < 9) {
        // 目标月在9月之前,从m月累加到8月
        for (int i = m; i < 9; i++) {
            total_days += month_days[i];
        }
        // 计算相对于基准的星期偏移:往前推total_days天
        // 往前推 total_days 天,计算新的星期偏移  
        // 7 代表星期日,公式保证结果落在 1~7 之间  
        start_day = 7 - (total_days - 1) % 7;
    } else {
        // 目标月在9月之后,从9月累加到m-1月
        for (int i = 9; i < m; i++) {
            total_days += month_days[i];
        }
        // 往后推total_days天
        start_day += total_days % 7;
    }

    // 打印该月日历:循环总格数 = 月初偏移 + 该月天数
    for (int i = 1; i <= month_days[m] + start_day - 1; i++) {
        if (i < start_day) {
            std::cout << "   " << " ";  // 月初前的空白天
        } else {
            printf("% 3d ", i - start_day + 1);  // 右对齐输出日期
        }
        if (i % 7 == 0) {
            std::cout << "\n";  // 每7列换行
        }
    }
    return 0;
}

本文由coderli.com原创,按照CC BY-NC-SA 4.0 进行授权

所有代码已上传至Github:https://github.com/lihongzheshuai/yummy-code

luogu-”系列题目可在 洛谷题库 在线评测。

bcqm-”系列题目可在 编程启蒙题库 在线评测。

GESP/CSP 认证学习微信公众号
GESP/CSP 认证学习微信公众号
最后更新于