首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >日期计算(蓝桥必备)

日期计算(蓝桥必备)

作者头像
十二.
发布2025-10-22 14:21:07
发布2025-10-22 14:21:07
1690
举报

日期计算属于蓝桥杯中的高频考点,以下几道算法能让大家轻松掌握日期计算

  • 题型特点:通常要求根据给定的日期,计算相隔的天数、星期几的变化,或者根据特定条件推算出某个日期等,一定要注意好年份(不同年,具体的日期就不同)
  • 对应知识点
    • 历法知识:需要了解公历的基本规则,如闰年的判断规则(普通年份(2月29天)能被 4 整除但不能被 100 整除的为闰年(2月28天),世纪年份能被 400 整除的是闰年),每个月的天数等。平年365天闰年366天。
    • 数学运算:涉及到日期之间的差值计算,可能需要进行年、月、日的分别运算和调整,有时还需要考虑跨年度、跨月份的情况。
    • 数据处理:可能会涉及到将输入的日期数据进行解析和处理,例如将字符串形式的日期转换为可以进行计算的数字形式。

学习途径:

1、跑步(蓝桥真题)

2、跑步计划(蓝桥真题)

3、定时任务(蓝桥真题)

4、神奇闹钟(蓝桥真题)

5、扫把扶不扶(算法赛)

1、跑步

跑步 本题为填空题,只需要算出结果后,在代码中使用输出语句将所填结果输出即可。 问题描述 小蓝每周六、周日都晨跑,每月的 11、1111、2121、3131 日也晨跑。其它时间不晨跑。 已知 20222022 年 11 月 11 日是周六,请问小蓝整个 20222022 年晨跑多少天? 运行限制

  • 最大运行时间:1s
  • 最大运行内存: 256M

运用到了知识点:

代码语言:javascript
复制
#include <iostream>
using namespace std;
int main()
{
  // 首先判断是否为闰年,不是闰年,2月有29天
  int day = 2;
  int flag = 0;
  // 本题主打合理运用数据进行计算
  int monthDay[] = {31,29,31,30,31,30,31,31,30,31,30,31};
  for(int i=0; i<sizeof(monthDay)/sizeof(monthDay[0]); ++i){ 
      for(int j=0; j<monthDay[i]; ++j){
        if(i==0&&(j==1||j==2)) continue; // 跳过重复计算
        flag++;
        if(flag%6==0 || flag%7==0 || j+1==11 || j+1==1|| j+1==21 || j+1==31) day++;
      }
  }
  cout<<day<<endl;

  return 0;
}

// 数组定义的三种格式
// 数据类型 数组名[长度]
// 数据类型 数组名[数组长度] = { 值1,值2 ... };
// 数据类型 数组名[] = { 值1,值2 ... };

在做跑步,这道题目时,我初步接触日期计算这种题型。

闰年、平年的运用,数组长度大小的计算 (复习+开拓)


2、跑步计划

问题描述 小蓝计划在某天的日期中出现 11 时跑 55 千米,否则只跑 11 千米。注意日期中出现 11 不仅指年月日也指星期。 请问按照小蓝的计划,20232023 年小蓝总共会跑步锻炼多少千米?例如,55 月 11 日、11 月 1313 日、1111 月 55 日、44 月 33 日 (星期一) 小蓝会跑 55 千米,而 55 月 2323 日小蓝会跑 11 千米 (示例日期均为 20232023 年) 答案提交 这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。

代码语言:javascript
复制
#include <iostream>
using namespace std;
int main()
{
    int s = 0;
    int monthDay[] = {31,28,31,30,31,30,31,31,30,31,30,31}; // 每月日期
    // 需考虑 3 种情况
    // 1、月份
    // 2、日期
    // 3、星期 , 找准1月1,是几月几日非常重要
    int flag = 0;
    for(int i=0; i<sizeof(monthDay)/sizeof(monthDay[0]); ++i){
        for(int j=0; j<monthDay[i]; ++j){
            flag++;
            if(i+1==1 || i+1>=10){ // 月份
                s += 5;
            }else if(j+1==1 || (10<=j+1 && j+1<=19) || j+1 == 21 || j+1 == 31){ // 日
                s += 5;
            }else if(flag%7==2){ // 星期
                s += 5;
            }else{
                s++;
            }
        }
    }
    cout<<s<<endl;
    return 0;
}

在单纯的日期里面,开始涉及星期。


3、定时任务

问题描述

Cron 表达式在定时任务中经常被使用,在这里我们用了一种简化后的版本 SimpleCron 表达式:SimpleCron 表达式是一个具有时间含义的字符串,字符串以 44 个空格隔开,分为 55 个域,格式为 XXXXXXXXXX,其中 XX 是一个域的占位符。55 个域从左至右依次为秒 (0−59)(0−59)、分钟 (0−59)(0−59)、小时 (0−23)(0−23)、日期 (1−31)(1−31)、月份 (1−12)(1−12),其中括号内为他们各自的取值范围。同时域内取值也可以使用一些特殊字符(每个域内只能使用一种特殊字符):

  1. 特殊字符 ∗∗ (ASCII 码为 4242)表示所有可能的值。例如:在分钟域内表示每一分钟;在日期域内表示月内的每一天。
  2. 特殊字符 ,, (ASCII 码为 4444)表示列出枚举值。例如:在秒域内,3,203,20 表示分别在 33 秒和 2020 秒执行一次任务。
  3. 特殊字符 −− (ASCII 码为 4545)表示范围,可以视为连续的若干个枚举值。例如:1−51−5 等价于 1,2,3,4,51,2,3,4,5。

例如,421,3,151−31∗421,3,151−31∗表示的含义是每个月份中的每一天中的 01:02:0401:02:04、03:02:0403:02:04、15:02:0415:02:04 这三个时刻各执行一次,在 20232023 年一共会执行 10951095 次。

现在给出你一个合法的 SimpleCron 表达式,其中用到的所有数字均没有前导零。请问在 20232023 一整年当中,使用了这个表达式的定时任务总计会执行多少次?

输入格式

输入一行,包含一个 SimpleCron 字符串。

输出格式

输出一行,包含一个整数表示答案。

样例输入
代码语言:javascript
复制
4 2 1,3,15 1-31 *
样例输出
代码语言:javascript
复制
1095
评测用例规模与约定

对于所有评测用例,0≤0≤ 秒域的取值 ≤59≤59,0≤0≤ 分钟域的取值 ≤59≤59,0≤0≤ 小时域的取值 ≤23≤23,1≤1≤ 日期域的取值 ≤31≤31,1≤1≤ 月份域的取值 ≤12≤12。

// 本题我最开始的想法就是暴力破解,但是暴力破解本题,是何等的烧脑,因为用单纯的数字表示,是苍白无力的!

// 这是就体验到了,数据结构的强大,而本题就巧妙运用vector中的.size(),简单的代替了数字,虽然它只是入门级别的。

代码语言:javascript
复制
#include "bits/stdc++.h"
using namespace std;

// 我很佩服这道题,缘由是因为,它通过vector巧妙地代替了数字,合理的搭配、vector的巧妙运用与for-range的运用,让人感觉道赏心悦目

vector<int> generateNum(string str, int range){ // 生成数量
    vector<int> res;
    // (每个域内只能使用一种特殊字符)
    int cur=0;
    int lis = -1; // 作用于-时,
    for(char c : str){
        if(c==','){
            res.push_back(cur);
            cur = 0;
        }else if(c=='-'){
            lis = cur;
            cur = 0;
        }else if(c=='*'){
            for(int i=0; i<range; i++) res.push_back(i+1);
            return res;
        }else{ // 这个用来计算时间
            cur = cur*10 + c-'0';
        }
    }
    // 存入
    if(lis!=-1){
        for(int i=lis; i<=min(range,cur); ++i) res.push_back(i);
    }else{
        res.push_back(cur);
    }
    return res;
}

int main()
{
    int monthDay[] = {31,28,31,30,31,30,31,31,30,31,30,31}; // 每个月份,对应的天数

    // 用集合代替,次数,简直是一个天才
    string S,M,H,D,MON; cin>>S>>M>>H>>D>>MON; // 输入所有

    // 计算秒、分、时的可能
    int num1 = generateNum(S,60).size()*generateNum(M,60).size()*generateNum(H,24).size();

    // 对月份也是有要求的
    int num2 = 0;
    for(int i : generateNum(MON,12)){
        // 每个i都是月份
        num2+= generateNum(D,monthDay[i-1]).size()*num1;
    }
    cout<<num2<<endl;

    return 0;
}

用vector代表了每个区间的数量/次数,这种替换,让人感到心旷神怡。


4、神奇闹钟

问题描述

小蓝发现了一个神奇的闹钟,从纪元时间(19701970 年 11 月 11 日 00:00:0000:00:00)开始,每经过 xx 分钟,这个闹钟便会触发一次闹铃 (纪元时间也会响铃)。这引起了小蓝的兴趣,他想要好好研究下这个闹钟。

对于给出的任意一个格式为 уууу-MM-ddHH:mm:ssуууу-MM-ddHH:mm:ss 的时间,小蓝想要知道在这个时间点之前 (包含这个时间点) 的最近的一次闹铃时间是哪个时间?

注意,你不必考虑时区问题。

输入格式

输入的第一行包含一个整数 TT,表示每次输入包含 TT 组数据。

接下来依次描述 TT 组数据。

每组数据一行,包含一个时间(格式为 уууу-MM-ddHH:mm:ssуууу-MM-ddHH:mm:ss)和一个整数 xx,其中 xx 表示闹铃时间间隔(单位为分钟)。

输出格式

输出 TT 行,每行包含一个时间(格式为 уууу-MM-ddHH:mm:ssуууу-MM-ddHH:mm:ss),依次表示每组数据的答案。

样例输入
代码语言:javascript
复制
2
2016-09-07 18:24:33 10
2037-01-05 01:40:43 30
样例输出
代码语言:javascript
复制
2016-09-07 18:20:00
2037-01-05 01:30:00
评测用例规模与约定

对于所有评测用例,1≤T≤10,1≤x≤10001≤T≤10,1≤x≤1000,保证所有的时间格式都是合法的。

代码语言:javascript
复制
#include "bits/stdc++.h"
#define ll long long
using namespace std;

ll generate(int y,int M,int d,int h,int m,int s,int sub){ // 这里面有一个精致的细节,时间是从1月1日开始,而非0点0时0分!
    int D = 0; // 计算天数
    for(int i=1970; i<=y-1; ++i){ // 算年
        if( (i%4==0 && i%100!=0) ||  i%400==0){ // 是闰年,!!!这个判断相当重要
            D+=366;
        }else{ // 是平年
            D+=365;
        }
    }
    for(int i=1; i<=M-1; ++i){ // 算月!!!是到 M-1月,否则会多运算
        if(i==1 || i==3 || i==5 || i==7 || i==8 || i==10 || i==12) D+=31;
        else if(i==2&&((y%4==0 && y%100!=0) || y%400==0)) D+=29;
        else if(i==2) D+=28;
        else D+=30;
    }
    D+=(d-1); // 算日

    // 返回分钟
    ll num = D*(24*60) + h*60 + m;

    return num;
}

// 从新来过
// 这次从新来过
int main(){
    int t;
    cin>>t;
    while(t--){
        // 获得时间差
        int y,M,d,h,m,s,sub;
        scanf("%d-%d-%d %d:%d:%d %d",&y,&M,&d,&h,&m,&s,&sub);
        // 消减时间差
        ll all_min = generate(y,M,d,h,m,s,sub); // 生成总时间
        ll acc_time = all_min - (all_min%sub);

        // 现在已经取到时间,仅需顺流而上即可
        ll lday,lh,lm;
        lday = acc_time/(24*60); // 求出天数
        lh = acc_time/(60)%24; // 求出小时
        lm = acc_time%(60);// 求出分钟

        ll YYYY = 1970;
        ll MM = 1;
        ll dd = 1;
        ll HH = lh;
        ll mm = lm;
        ll ss = 0;

        while(lday>=0){ // 加年
            if( (YYYY%4==0 && YYYY%100!=0) || YYYY%400==0){
                if(lday<366) break;
                lday-=366;
            }else{
                if(lday<365) break;
                lday-=365;
            }
            YYYY++;
        }

        while(lday>0){ // 加月
            if(MM==1 || MM==3 || MM==5 || MM==7 || MM==8 || MM==10 || MM==12){ // 31天的月
                if(lday<31) break;
                lday-=31;
            }else if(MM==2 &&((YYYY%4==0 && YYYY%100!=0) || YYYY%400==0)){ // 闰年
                if(lday<29) break;
                lday-=29;
            }else if(MM==2){
                if(lday<28) break;
                lday-=28;
            }else{
                if(lday<30) break;
                lday-=30;
            }
            MM++; // 说实话,我对这一步,表示有点无法理解
        }
        dd += lday;
        printf("%04lld-%02lld-%02lld %02lld:%02lld:%02lld\n",YYYY,MM,dd,HH,mm,ss); // 切记,这里一定要换行
    }

    return 0;
}

// 记录,如何判断,闰年中的闰月
// 劝告,最好封装一下函数,因为越多的代码,就意味着可能有更多的纰漏
// 不过咱们那个时区计算,好像可有可无

其实本题,这样写,不是我心中,最好的解法,因为模拟时,需要用到有些重复代码很多次,固可分割组装成函数,以下是封装后的代码。

代码语言:javascript
复制
bool isLeapYear(int year){
    return ((year%4==0 && year%100 !=0) || year%400 == 0);
}

int getDaysInMonth(int year, int month){
    if(month==1 || month==3 || month==5 || month==7 || month==8 || month==10 || month==12) return 31;
    if(month==2) return isLeapYear(year) ? 29 : 28;
    return 30;
}

5、扫把扶不扶【算法赛】

问题描述 你正在参加一场程序员的终极面试,和你竞争的是小蓝。你们都顺利地通过了前几轮筛选,来到了最后一轮的现场面试环节。 你到达公司门口的时间是 S1S1​,和面试官约定的面试时间是 S2S2​。在约定时间到达前(S1∼S2S1​∼S2​),你可以选择提前开始面试。然而,如果超过了面试时间,即便只晚了 11 秒,你都将失去面试机会,直接被淘汰。 你的面试会持续 TT 分钟。 小蓝到达公司门口的时间是 S3S3​,和面试官约定的面试时间是 S4S4​。由于你在前几轮的表现比他好,因此小蓝必须在你结束面试后才能开始他的面试。如果小蓝在他的面试时间之前没有开始面试,他也将失去面试机会,直接被淘汰。 面试存在竞争机制:如果只有你或小蓝中的一人参与了面试,那么参与面试的那个人将胜出。如果你和小蓝都没有参与面试,你们将双双失败。 “那如果两个人都参与了面试,谁将胜出呢?”你正想着,突然发现公司门口的不远处躺着一把扫把,看样子是面试官故意放的。你想扶起它,但这需要你花费 XX 分钟的时间。 根据你所了解的套路,如果你扶起了扫把,并参加了面试,那么你和小蓝的竞争中,你必定能够胜出。相对的,如果你没有扶起扫把,并且你和小蓝都参加了面试,那么小蓝必定能够胜出。 现在,你和小蓝都会采取最优的策略来确保自己胜出(如果无论如何也无法使自己胜出,则应优先确保双双失败)。请问最后的结果会是如何? 输入格式 输入包含多组数据。 第一行包含一个整数 NN(1≤N≤1031≤N≤103),表示数据的组数。 接下来 NN 组数据,每组数据包含三行:

  • 第一行包含两个时间字符串 S1S1​, S2S2​,以空格分隔,表示你到达公司门口的时间和你和面试官约定的面试时间。
  • 第二行包含两个时间字符串 S3S3​, S4S4​,以空格分隔,表示小蓝到达公司门口的时间和小蓝和面试官约定的面试时间。
  • 第三行包含两个整数 T,xT,x(1≤T,x≤1201≤T,x≤120),以空格分隔,分别表示你的面试时长和你扶起扫把所需的时间(单位:分钟)。

时间的格式为 HH:MM:SSHH:MM:SS,其中 HHHH 表示小时(00≤HH≤2100≤HH≤21),MMMM 表示分钟(00≤MM≤5900≤MM≤59),SSSS 表示秒(00≤SS≤5900≤SS≤59)。 输出格式 对于每组数据,输出一个字符串,表示最终的结果。

  • 如果你胜出了,输出 You
  • 如果小蓝胜出了,输出 Lan
  • 如果你们双双失败了,输出 Draw
代码语言:javascript
复制
#include <iostream>
using namespace std;
int main()
{

    int t;
    cin>>t;
    while(t--){
        int yh1,ym1,ys1,yh2,ym2,ys2;
        int lh1,lm1,ls1,lh2,lm2,ls2;
        int T,x;
        scanf("%d:%d:%d %d:%d:%d",&yh1,&ym1,&ys1,&yh2,&ym2,&ys2);
        scanf("%d:%d:%d %d:%d:%d",&lh1,&lm1,&ls1,&lh2,&lm2,&ls2);
        scanf("%d %d",&T,&x);

        // 将四个值转化为时间,一秒为基础单位
        int in1,start1;
        int in2,start2;
        in1 = yh1*60*60 + ym1*60 + ys1;
        start1 = yh2*60*60 + ym2*60 + ys2;
        in2 = lh1*60*60 + lm1*60 + ls1;
        start2 = lh2*60*60 + lm2*60 + ls2;
        T = T*60;
        x = x*60;

        // You局
        // 扶起了扫把
        // 面试结束时间超过小蓝面试时间
        // 小蓝迟到了,You没迟到
        if(start1>=in1){
            if(start1-in1 >= x || start1+T > start2 || in2 > start2){
                cout<<"You"<<endl;
                continue;
            }
        }

        // Draw局
        // 你俩都迟到了
        if(start1<in1 && start2<in2){
            cout<<"Draw"<<endl;
            continue;
        }


        // Lan局
        // 你迟到了
        // 小蓝参与了
        if(start2>=in2){
            cout<<"Lan"<<endl;
            continue;
        }
    }


    return 0;
}

这是一道模拟+日期计算题,从scanf(); 的格式输入,到对闰年平年的判断。 如:MM==2 &&((YYYY%4==0 && YYYY%100!=0) || YYYY%400==0) 到最后对函数的封装,三元运算符的应用。简直完美。


简单的题,不一定简单。


笔者感悟:


做题时,不要害怕自己的想法不是最优解,因为答案也不一定是最优解,而是应该先自己做一遍,大概15min作用,无思路,在借鉴高质量答案,将自己与它的不同标志。长期这样可培养独立思考能力。


本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-02-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 学习途径:
  • 1、跑步
  • 2、跑步计划
  • 3、定时任务
    • 问题描述
    • 输入格式
    • 输出格式
    • 样例输入
    • 样例输出
    • 评测用例规模与约定
  • 4、神奇闹钟
    • 问题描述
    • 输入格式
    • 输出格式
    • 样例输入
    • 样例输出
    • 评测用例规模与约定
  • 5、扫把扶不扶【算法赛】
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档