回文日期问题
问题详情
在日常生活中,通过年、月、日这三个要素可以表示出一个唯一确定的日期。
牛牛习惯用 8
位数字表示一个日期,其中,前 4
位代表年份,接下来 2
位代表月份,最后 2
位代表日期。
显然:一个日期只有一种表示方法,而两个不同的日期的表示方法不会相同。
牛牛认为,一个日期是回文的,当且仅当表示这个日期的 8
位数字是回文的。
现在,牛牛想知道:在他指定的两个日期之间(包含这两个日期本身),有多少个真实存在的日期是回文的。
一个 8 位数字是回文的,当且仅当对于所有的 i(1≤i≤8) 从左向右数的第 i 个数字和第 9−i个数字(即从右向左数的第 i个数字)是相同的。
例如:
对于 2016 年 11 月 19 日,用 8 位数字 2016111 表示,它不是回文的。
对于 2010 年 1 月 2 日,用 8 位数字 20100102 表示,它是回文的。
对于 2010 年 10 月 2 日,用 8 位数字 20101002 表示,它不是回文的。
输入格式
输入包括两行,每行包括一个 8
位数字。
第一行表示牛牛指定的起始日期 date1,第二行表示牛牛指定的终止日期 date2
。保证 date1 和 date2 都是真实存在的日期,且年份部分一定为 4 位数字,且首位数字不为 0
保证 date1 一定不晚于 date2
输出格式
输出共一行,包含一个整数,表示在 date1 和 date2之间,有多少个日期是回文的。
输入样例:
20110101
20111231
输出样例:
1
问题分析(日期问题详解)
1、年
考虑到 闰年 的特殊情况,该年会影响月份,并且年份的限制相较于月、日最少,所以我们从年开始分析;
2、月、日
枚举每个月份的天数
int months[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};
这里为了方便下标与月份相对应,我们将 数组 的实际有用下标右移 1
【这里列举的是非闰年,闰年情况后面会提到】
3、表示方法
int year=date/10000;
int month=date%10000/100;
int day=date%100;
这里是 XXXX–XX–XX 的表示法;
4、判断方法
4.1
首先排除掉 月份 为 0 ,日期 为 0 的情况,再排除月份大于 12 的情况
【这里天数是否超标由月份决定相对复杂,这里先不讨论,先从简单的问题入手】
if(!month || month > 12 || !day) return false;
4.2
接着开始讨论每月的天数,这里先不讨论 二月【这里涉及到闰年】
if(month != 2 && day > months[month]) return false;
4.3
讨论闰年的情况,首先要判断闰年
bool is_leap= (year % 4 ==0 && year%100) || (year %400 ==0);
再根据闰年来决定2月份对应的天数
if(day>is_leap+28){
return false;
}
5、利用镜面对称构建符合部分条件的日期
5.1
枚举 1 ~ 9999 表示 年,为什么先表示年呢? — 年份没有限制
初始化 date 表示年
int date= i ;
构建一个 x 负责制造镜面对称,制造方法如下:
for(int j=0;j<4;j++){
date=date*10+x%10;
x/=10;
}
千 百 十 个 ------- | -------- 个 十 百 千
这里是指date -------- -----这里由循环产生的x1,x2,x3,x4组成
6.方法总结
就是由限制条件最少的点入手,先从年开始,再考虑月,逐步排除不符合题意的情况,得到解;
bool check(int date )
{
int year=date/10000;
int month=date%10000/100;
int day=date%100;
if(!month || month > 12 || !day) return false;
if(month != 2 && day > months[month]) return false;
if (month == 2)
{
bool is_leap= (year % 4 ==0 && year%100) || (year %400 ==0);
if(day>is_leap+28){
return false;
}
}
return true;
}
完整代码
#include<iostream>
using namespace std;
int months[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};
bool check(int date )
{
int year=date/10000;
int month=date%10000/100;
int day=date%100;
if(!month || month > 12 || !day) return false;
if(month != 2 && day > months[month]) return false;
if (month == 2)
{
bool is_leap= (year % 4 ==0 && year%100) || (year %400 ==0);
if(day>is_leap+28){
return false;
}
}
return true;
}
int main(){
int start,end;
cin>>start>>end;
int res=0;
for(int i=0;i<10000;i++){//这里的 i 枚举的是年份,所以没有范围限定
int x=i,r=i;
for(int j=0;j<4;j++){
r=r*10+x%10;
x/=10;
}
if(r>=start && r<=end && check(r) ) res++;
//最后判断构建的日期是否在给定日期的范围内
}
cout<<res;
return 0;
}