回文日期(NOIP2016)
原创
©著作权归作者所有:来自51CTO博客作者Java大神的原创作品,请联系作者获取转载授权,否则将追究法律责任
题目:回文日期
这题虽然说不难,但是也不能算水了。
我先讲讲思路。60分的算法很好写,就是判断一下是不是回文串,分离每个数位,判断即可。
但我们的目标是满分,所以我来讲讲满分算法。
首先,给的是区间,那么,难免的,我们需要去枚举,但是怎么枚举?
一天一天的加然后判断?想想都可怕,一定不是这样。那么是怎样的呢?
考虑到,一共八位,回文需要前后四位对称,所以,日期不行,我们对称过去,是什么?年!我们以年为单位枚举,就要少了很多无用的枚举。
所以我们的思路是,枚举两个年限之间的每个年(包括端点),然后对称得到月日,判断是否符合日期规则就行了,同时还要注意,在端点年,要判断日期是否在区间内。
下面看代码:
#include<bits/stdc++.h>
using namespace std;
int aa[8],bb[8],cc[4];
int main(){
int a,b;
scanf("%d%d",&a,&b);
int ans=0;
int ya=(a-a%10000)/10000; //1
int yb=(b-b%10000)/10000;
for(int i=0;i<8;i++){ //2
aa[i]=a%10;
a/=10;
bb[i]=b%10;
b/=10;
}
int ma=aa[3]*10+aa[2]; //3
int mb=bb[3]*10+aa[2];
int da=aa[1]*10+aa[0];
int db=bb[1]*10+bb[0];
for(int i=ya;i<=yb;i++){ //4
int x=i;
for(int j=0;j<4;j++){ //5
cc[j]=x%10;
x/=10;
}
int mon=cc[0]*10+cc[1]; //6
int day=cc[2]*10+cc[3];
//特判
if(i==ya){ //7
if(!((mon>ma)||(mon==ma&&day>=da))){
continue;
}
}else if(i==yb){
if(!((mon<mb)||(mon==mb&&day<=db))){
continue;
}
}
if(mon>=1&&mon<=12){ //8
if(mon==1||mon==3||mon==5||mon==7||mon==8||mon==10||mon==12){
if(day>=1&&day<=31){
ans++;
}
}else if(mon==2){
if((i%4==0&&i%100!=0)||i%400==0){
if(day>=1&&day<=29){
ans++;
}
}else{
if(day>=1&&day<=28){
ans++;
}
}
}else{
if(day>=1&&day<=30){
ans++;
}
}
}
}
printf("%d",ans);
return 0;
}
代码中标了8个位置,我们下面讲一下。
1处:计算端点年。用取模实现。
2处:将输入日期数位分离,便于3计算。
3处:计算端点月份和日期,便于7处的特判。
4处:枚举闭区间之间的每个年份。
5处:对于每一个枚举的年份,分离数位,便于计算对称后的月和日。
6处:计算对称后的月和日。
7处:特判,如果是端点值,在此判断是否在区间时间内,如果不是,跳过此次循环,如果是继续向下。
8处:依次判断月和日是否合法,月在1到12之间,还要依据月判断时间,注意闰年,就行了。