目录
[链接:2867. 回文日期 - AcWing题库](#链接:2867. 回文日期 - AcWing题库)
[链接:3498. 日期差值 - AcWing题库](#链接:3498. 日期差值 - AcWing题库)
日期问题在算法初学时,可所谓屡见不鲜。因为算法初学者在写算法入门题时,会发现做的最多的题就是模拟题,那么模拟题最喜欢的出的就是日期问题。日期问题分为好几个具体的问题,比如:闰年判断、回文日期、日期差值等。本篇博客会带领大家详细讲解日期问题。
日期问题概述
日期问题见名知意,就是有关时间的问题,出题人在时间这一个角度去做文章,现在来看各大刷题网站,什么样的时间问题都有,比如闰年判断、回文日期、日期差值、日期格式化输入输出、时间轮询、时间窗口问题等。可所谓眼花缭乱,但是有的时间问题感觉就是图一乐,但是不排除创新题的可能性,后续也没有什么作用,既不会考也不会出在面试题上,下面我会对几种常见的日期问题进行详解。
一、闰年判断
问题描述:
一般题目会给你一个年份,或者让你输入一个年份,让你去判断这个年份是不是闰年。
解决方法:
闰年判断,只要牢记这一句话就可以,能够被4整除并且不能被100整除或者能够被400整除。就是只要满足下面任意一个条件就可以。
1.年份能够被4整除但是不能被100整除 --> year%4==0&&year%100!=0
2.年份能够被400整除 --> year%400==0
代码实现:
cpp
#include<stdio.h>
int main(){
int year;
scanf("%d",&year);
if(year%4==0&&year%100!=0||year%400==0){//条件
printf("%d is a leap year\n",year);
}else{
printf("%d is not a leap year\n",year);
}
return 0;
}
二、回文日期
问题描述:
回文日期就是我们C语言课上经常说的回文数,类似于123321、12321等这样对称的数,那么在日期问题上,可以做很多扩展。这种问题看上去很简单,但是属于大模拟问题,写的代码比较长,因为我们既要根据月份不同给增加的天数不同,2月份天数还要根据闰年定。下面我们以acwing上的一道题为例子进行讲解回文日期问题。
链接:2867. 回文日期 - AcWing题库
2020 年春节期间,有一个特殊的日期引起了大家的注意:2020 年 2 月 2 日。
因为如果将这个日期按 "yyyymmdd" 的格式写成一个 8 位数是 20200202,恰好是一个回文数。
我们称这样的日期是回文日期。
有人表示 20200202 是"千年一遇" 的特殊日子。
对此小明很不认同,因为不到 22 年之后就是下一个回文日期:20211202 即 2021 年 12 月 2 日。
也有人表示 20200202 并不仅仅是一个回文日期,还是一个 ABABBABA 型的回文日期。
对此小明也不认同,因为大约 100 年后就能遇到下一个 ABABBABA 型的回文日期:21211212 即 21212121 年 12 月 12 日。
算不上"千年一遇",顶多算"千年两遇"。
给定一个 8 位数的日期,请你计算该日期之后下一个回文日期和下一个 ABABBABA 型的回文日期各是哪一天。
注意: 本题数据保证一定有解。
注意
下一个回文日期和下一个 ABABBABA 型的回文日期可能是同一天。
ABABBABA 型的回文日期,需要满足 A≠B。
输入格式
输入包含一个八位整数 N,表示日期。
输出格式
输出两行,每行 1 个八位数。
第一行表示下一个回文日期,第二行表示下一个 ABABBABA 型的回文日期。
数据范围
对于所有评测用例,10000101≤N≤89991231,保证 N 是一个合法日期的 88 位数表示。
输入样例:
cpp20200202
输出样例:
cpp20211202 21211212
解决方法:
题目要求我们寻找下一个回文日期跟ABABBABA 型的回文日期,那么我们就要在一次搜索中同时判断此两个回文日期。
我们一般会根据天数设置一个月份的天数的数组(2月份28天),那么涉及到2月份就要判断闰年,我们再写一个函数去判断闰年。我们根据输入的日期每一次去递增1去寻找回文日期。当两个符合条件的日期循环才可以停,当递增1时,可能引起天数、月份的进位,那么我们要去写一个获取天数的函数,当天数大于此月份的天数,那么月份+1,月份越界了,年份+1。
代码实现:
cpp
#include<iostream>
using namespace std;
bool found1=false,found2=false;//found1为下一个回文日期判断是否找到,found2ABABBABA 型的回文日期
int months[]={0,31,28,31,30,31,30,31,31,30,31,30,31};//月份天数数组
int is_leap(int year){//判断是否为闰年
if(year%4==0&&year%100||year%400==0){
return 1;
}
return 0;
}
int get_day(int year,int month){//获取天数
if(month==2){
return 28+is_leap(year);
}
return months[month];
}
void next_day(int &y,int &m,int &d){//自增到下一天
d++;
if(d>get_day(y,m)){
d=1;
m++;
if(m>12){
m=1;
y++;
}
}
}
bool cheak1(char s[]){//判断日期是否为回文日期
for(int i=0,j=7;i<j;i++,j--){
if(s[i]!=s[j]){
return false;
}
}
return true;
}
bool cheak2(char s[]){//判断是否为ABABBABA 型的回文日期
char a=s[0];
char b=s[1];
char c=s[2];
char d=s[3];
if(a==c&&b==d&&a!=b){
return true;
}
return false;
}
int main(){
int y,m,d;
char s[10];//中介作用,负责转化
scanf("%04d%02d%02d",&y,&m,&d);
while(!found1||!found2){
next_day(y,m,d);
sprintf(s,"%04d%02d%02d",y,m,d);
if(cheak1(s)){
if(!found1){
found1=true;
puts(s);
}
if(!found2&&cheak2(s)){
found2=true;
puts(s);
}
}
}
return 0;
}
三、日期差值
问题描述:
日期差值,顾名思义,给定两个日期,让我们计算这两个日期之前有多少天。那么这种问题看上去很简单,但是也是属于大模拟问题,因为,我们要考虑12个月份天数的不同,当需要借位时,我们还要根据上一个月不同的天数给增加多少天,下面我们以acwing上的一道题为例题,进行讲解。
链接:3498. 日期差值 - AcWing题库
有两个日期,求两个日期之间的天数,如果两个日期是连续的我们规定他们之间的天数为两天。
输入格式
输入包含多组测试数据。
每组数据占两行,分别表示两个日期,形式为 YYYYMMDD。
输出格式
每组数据输出一行,即日期差值。
数据范围
年份范围 [1,9999]
保证输入日期合法。
测试数据的组数不超过 100
输入样例:
bash20110412 20110422
输出样例:
bash11
解决方法:
求两个日期之前的差值,模拟的做法就是y2-y1,m2-m1,d2-d1,思路是这样的,但是这样需要考虑借位、闰年等,比较麻烦,那么我们可以转变思路求出第一年1月1日起到y1年m1月d1日共多少天为t1天,第一年1月1日起到y2年m2月d2日共多少天,为t2天,|t2-t1|+1即为答案。求多少天时,要先求y1-1年,m-1月,共多少天,再加上d1即可。上代码。
代码实现:
cpp
#include<iostream>
using namespace std;
int months[]={0,31,28,31,30,31,30,31,31,30,31,30,31};//一年365天月份数组
int is_leap(int year){//判断是否为闰年
if(year%4==0&&year%100||year%400==0){
return 1;
}else{
return 0;
}
}
int getday(int year,int month){//获取哪一年哪一月的天数
if(month==2)return 28+is_leap(year);//判断闰年二月份29天
return months[month];
}
int calc(int year,int month,int day){//1~year年month月day日共多少天
int ans=0;
for(int i=1;i<year;i++){
ans+=365+is_leap(i);//闰年多一天
}
for(int i=1;i<month;i++){
ans+=getday(year,i);
}
return ans+day;
}
int main(){
int y1,m1,d1,y2,m2,d2;
while(~scanf("%04d%02d%02d\n%04d%02d%02d",&y1,&m1,&d1,&y2,&m2,&d2)){//多组输入
//%04d表示输入为4位,不足4位前面补0
printf("%d\n",abs(calc(y1,m1,d1)-calc(y2,m2,d2))+1);//两个日期之差取绝对值
//+1,自己也是一天,比如1月1到1月2,这是两天,相减是一天,所以要加1
}
return 0;
}
四、日期格式化输入输出
问题描述:
当我们需要输入输出日期时,必须按照题目所给的格式进行输入输出,比如yyyy年mm月dd日,当年份只有三位时前面需要补0,比如0202年,月份为个位数月份时,前一位需要补0,例如01、03月,日期同理。
解决方法:
我们根据上面,可以发现规律,知道了输入输出的规律,年份为四位,不够前面补0;月份为2位,不够前面补0;天数为2位,不够前面补0。
要实现上面的规律我们就要在输入输出函数上做一下手脚。在c语言输出输出时%yd,在y处填入数字num,它会自动格式化为num位,那么%xyd,在x处填入数字tep,在y处填入数字num,它意味着输入输出的数字自动格式化为num位,如果不足num位前面补tep这个数。
代码实现:
cpp
scanf("%04d%02d%02d\n%04d%02d%02d",&y1,&m1,&d1,&y2,&m2,&d2)//输入,不足的补0
printf("%04d%02d%02d",y,m,d);//输出方法一,不足的补0
sprintf(s,"%04d%02d%02d",y,m,d);//输出方法二,先把ymd转化为格式化的字符串,再输出
puts(s);
日期问题在模拟题中还是比较多的,在算法竞赛中,像icpc、ccpc这种竞赛比较少,日期问题能创新的基本都创新了,没有什么新题可以出了。但是在蓝桥杯中,日期问题是热点问题,基本每一年都要考,参加蓝桥杯的同学还是很有必要好好学一下日期问题。本篇博客中涉及到的日期问题只解释了四个,还有很多日期问题没有写,希望大家在以后刷题中自己总结,这里不再过多解释。
执笔至此,感触彼多,全文将至,落笔为终,感谢大家的支持。