
日期计算属于蓝桥杯中的高频考点,以下几道算法能让大家轻松掌握日期计算
1、跑步(蓝桥真题)

跑步 本题为填空题,只需要算出结果后,在代码中使用输出语句将所填结果输出即可。 问题描述 小蓝每周六、周日都晨跑,每月的 11、1111、2121、3131 日也晨跑。其它时间不晨跑。 已知 20222022 年 11 月 11 日是周六,请问小蓝整个 20222022 年晨跑多少天? 运行限制
运用到了知识点:
#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 ... };在做跑步,这道题目时,我初步接触日期计算这种题型。
闰年、平年的运用,数组长度大小的计算 (复习+开拓)
问题描述 小蓝计划在某天的日期中出现 11 时跑 55 千米,否则只跑 11 千米。注意日期中出现 11 不仅指年月日也指星期。 请问按照小蓝的计划,20232023 年小蓝总共会跑步锻炼多少千米?例如,55 月 11 日、11 月 1313 日、1111 月 55 日、44 月 33 日 (星期一) 小蓝会跑 55 千米,而 55 月 2323 日小蓝会跑 11 千米 (示例日期均为 20232023 年) 答案提交 这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
#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;
}在单纯的日期里面,开始涉及星期。
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),其中括号内为他们各自的取值范围。同时域内取值也可以使用一些特殊字符(每个域内只能使用一种特殊字符):
例如,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 字符串。
输出一行,包含一个整数表示答案。
4 2 1,3,15 1-31 *1095对于所有评测用例,0≤0≤ 秒域的取值 ≤59≤59,0≤0≤ 分钟域的取值 ≤59≤59,0≤0≤ 小时域的取值 ≤23≤23,1≤1≤ 日期域的取值 ≤31≤31,1≤1≤ 月份域的取值 ≤12≤12。
// 本题我最开始的想法就是暴力破解,但是暴力破解本题,是何等的烧脑,因为用单纯的数字表示,是苍白无力的!
// 这是就体验到了,数据结构的强大,而本题就巧妙运用vector中的.size(),简单的代替了数字,虽然它只是入门级别的。
#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代表了每个区间的数量/次数,这种替换,让人感到心旷神怡。
小蓝发现了一个神奇的闹钟,从纪元时间(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),依次表示每组数据的答案。
2
2016-09-07 18:24:33 10
2037-01-05 01:40:43 302016-09-07 18:20:00
2037-01-05 01:30:00对于所有评测用例,1≤T≤10,1≤x≤10001≤T≤10,1≤x≤1000,保证所有的时间格式都是合法的。
#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;
}
// 记录,如何判断,闰年中的闰月
// 劝告,最好封装一下函数,因为越多的代码,就意味着可能有更多的纰漏
// 不过咱们那个时区计算,好像可有可无其实本题,这样写,不是我心中,最好的解法,因为模拟时,需要用到有些重复代码很多次,固可分割组装成函数,以下是封装后的代码。
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;
}问题描述 你正在参加一场程序员的终极面试,和你竞争的是小蓝。你们都顺利地通过了前几轮筛选,来到了最后一轮的现场面试环节。 你到达公司门口的时间是 S1S1,和面试官约定的面试时间是 S2S2。在约定时间到达前(S1∼S2S1∼S2),你可以选择提前开始面试。然而,如果超过了面试时间,即便只晚了 11 秒,你都将失去面试机会,直接被淘汰。 你的面试会持续 TT 分钟。 小蓝到达公司门口的时间是 S3S3,和面试官约定的面试时间是 S4S4。由于你在前几轮的表现比他好,因此小蓝必须在你结束面试后才能开始他的面试。如果小蓝在他的面试时间之前没有开始面试,他也将失去面试机会,直接被淘汰。 面试存在竞争机制:如果只有你或小蓝中的一人参与了面试,那么参与面试的那个人将胜出。如果你和小蓝都没有参与面试,你们将双双失败。 “那如果两个人都参与了面试,谁将胜出呢?”你正想着,突然发现公司门口的不远处躺着一把扫把,看样子是面试官故意放的。你想扶起它,但这需要你花费 XX 分钟的时间。 根据你所了解的套路,如果你扶起了扫把,并参加了面试,那么你和小蓝的竞争中,你必定能够胜出。相对的,如果你没有扶起扫把,并且你和小蓝都参加了面试,那么小蓝必定能够胜出。 现在,你和小蓝都会采取最优的策略来确保自己胜出(如果无论如何也无法使自己胜出,则应优先确保双双失败)。请问最后的结果会是如何? 输入格式 输入包含多组数据。 第一行包含一个整数 NN(1≤N≤1031≤N≤103),表示数据的组数。 接下来 NN 组数据,每组数据包含三行:
时间的格式为 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。#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作用,无思路,在借鉴高质量答案,将自己与它的不同标志。长期这样可培养独立思考能力。