目录

日期问题概述

一、闰年判断

问题描述:

解决方法:

代码实现:

二、回文日期

问题描述:

链接:2867. 回文日期 - AcWing题库

解决方法:

代码实现:

三、日期差值

问题描述:

链接:3498. 日期差值 - AcWing题库

解决方法:

代码实现:

四、日期格式化输入输出

问题描述:

解决方法:

代码实现:


日期问题在算法初学时,可所谓屡见不鲜。因为算法初学者在写算法入门题时,会发现做的最多的题就是模拟题,那么模拟题最喜欢的出的就是日期问题。日期问题分为好几个具体的问题,比如:闰年判断、回文日期、日期差值等。本篇博客会带领大家详细讲解日期问题。

日期问题概述

日期问题见名知意,就是有关时间的问题,出题人在时间这一个角度去做文章,现在来看各大刷题网站,什么样的时间问题都有,比如闰年判断、回文日期、日期差值、日期格式化输入输出、时间轮询、时间窗口问题等。可所谓眼花缭乱,但是有的时间问题感觉就是图一乐,但是不排除创新题的可能性,后续也没有什么作用,既不会考也不会出在面试题上,下面我会对几种常见的日期问题进行详解。


一、闰年判断

问题描述:

一般题目会给你一个年份,或者让你输入一个年份,让你去判断这个年份是不是闰年。

解决方法:

闰年判断,只要牢记这一句话就可以,能够被4整除并且不能被100整除或者能够被400整除。就是只要满足下面任意一个条件就可以。

1.年份能够被4整除但是不能被100整除        --> year%4==0&&year%100!=0                      

2.年份能够被400整除                                   --> year%400==0

代码实现:

#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 位数表示。

输入样例:

20200202

输出样例:

20211202
21211212

解决方法:

题目要求我们寻找下一个回文日期跟ABABBABA 型的回文日期,那么我们就要在一次搜索中同时判断此两个回文日期。

我们一般会根据天数设置一个月份的天数的数组(2月份28天),那么涉及到2月份就要判断闰年,我们再写一个函数去判断闰年。我们根据输入的日期每一次去递增1去寻找回文日期。当两个符合条件的日期循环才可以停,当递增1时,可能引起天数、月份的进位,那么我们要去写一个获取天数的函数,当天数大于此月份的天数,那么月份+1,月份越界了,年份+1。

代码实现:

#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

输入样例:

20110412
20110422

输出样例:

11

解决方法:

求两个日期之前的差值,模拟的做法就是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即可。上代码。

代码实现:

#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这个数。

代码实现:

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这种竞赛比较少,日期问题能创新的基本都创新了,没有什么新题可以出了。但是在蓝桥杯中,日期问题是热点问题,基本每一年都要考,参加蓝桥杯的同学还是很有必要好好学一下日期问题。本篇博客中涉及到的日期问题只解释了四个,还有很多日期问题没有写,希望大家在以后刷题中自己总结,这里不再过多解释。

执笔至此,感触彼多,全文将至,落笔为终,感谢大家的支持。

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部