回文日期(保姆级教程)

回文日期问题

问题详情

在日常生活中,通过年、月、日这三个要素可以表示出一个唯一确定的日期。

牛牛习惯用 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;
}

  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蒜白

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值