回文日期
描述
传送门:我是传送门
在日常生活中,通过年、月、日这三个要素可以表示出一个唯一确定的日期。
牛牛习惯用8位数字表示一个日期,其中,前4位代表年份,接下来2位代表月 份,最后2位代表日期。显然:一个日期只有一种表示方法,而两个不同的日期的表 示方法不会相同。
牛牛认为,一个日期是回文的,当且仅当表示这个日期的8位数字是回文的。现 在,牛牛想知道:在他指定的两个日期之间包含这两个日期本身),有多少个真实存 在的日期是回文的。
一个8位数字是回文的,当且仅当对于所有的i(1<= i <= 8)从左向右数的第i个 数字和第9-i个数字(即从右向左数的第i个数字)是相同的。
例如:
•对于2016年11月19日,用8位数字20161119表示,它不是回文的。
•对于2010年1月2日,用8位数字20100102表示,它是回文的。
•对于2010年10月2日,用8位数字20101002表示,它不是回文的。
每一年中都有1212个月份:
其中,1,3,5,7,8,10,12月每个月有31天;4,6,9,11月每个月有30天;而对于2月,闰年时有29天,平年时有28天。
一个年份是闰年当且仅当它满足下列两种情况其中的一种:
1.这个年份是4的整数倍,但不是100的整数倍;
2.这个年份是400的整数倍。
例如:
•以下几个年份都是闰年:2000,2012,2016。
•以下几个年份是平年:1900,2011,2014。
输入
两行,每行包括一个8位数字。
第一行表示牛牛指定的起始日期。
第二行表示牛牛指定的终止日期。
保证date_i和都是真实存在的日期,且年份部分一定为4位数字,且首位数字不为0。
保证date 1 —定不晚于date 2。
输出
一个整数,表示在date1和date2之间,有多少个日期是回文的。
样例
输入
20110101
20111231
输出
1
Note
对于%60的数据,满足date1 = date2
实测date1 = date2的数据不足60%,也就是说本题的测试数据比正常的多一些,应该是比洛谷的数据要强
思路
显然,每年至多存在一个回文日期
因此只需要根据年份算出回文日期对应的月与日判断其是否合法便可以知道今年日否存在回文日期
具体步骤看代码,附详细注释
为优化阅读体验,已删除大量无关代码
难度大约为(区域赛签到题难度x1.0 ~ x1.2)
代码
/*
* ================================================================
*
* Filename: 1272.cpp
*
* Link: http://210.44.144.221/problem.php?id=1272
*
* Version: 1.0
* Created: 2018/10/26 18时02分08秒
* Revision: none
* Compiler: g++
*
* Author: 杜宁元 (https://duny31030.top/), duny31030@126.com
* Organization: QLU_浪在ACM
*
* ================================================================
*/
#include <bits/stdc++.h>
using namespace std;
// 存储每个月的天数
// 2月需要进行特判
int mon[15] = {0,31,28,31,30,31,30,31,31,30,31,30,31};
ll y,m,d,y2,m2,d2;
ll date1,date2;
// 判断year是否是闰年
bool ye(int year)
{
if((year%4 == 0 && year%100 != 0) || (year%400 == 0))
return true;
return false;
}
// 对于一个确定的日期进行检查
int check(int date)
{
// 取出这个确定的日期的每一位
// 共取出8位存储到tmp数组中
int tmp[10];
for(int i = 1;i <= 8;i++)
{
tmp[i] = date%10;
date /= 10;
}
int flag = 1;
for(int i = 1,j = 8;i <= 4;i++,j--)
{
// 如果有一个位置的数不同则构不成回文
if(tmp[i] != tmp[j])
flag = 0;
}
return flag;
}
// 对于一段日期进行检查
// 传入参数的说明
// year 年份
// m1 d1 开始的月份与日期
// m2 d2 结束的月份与日期
// 返回在year-m1-d1 - year-m2-d2这段日期内有没有回文日期
// 有 返回1
// 没有 返回0
// 原理:根据year来算出回文日期对应的月份t1与日子t2
// 首先判断这个日期是否合法
// 例如:00-00 13-40 这都是不合法的日期
// 01-31 12-21 是合法的日期
// 至于2-29这个日期是否合法需要进行特判看今年是否为闰年
// 然后检查t1-t2 是否在[m1-d1 - m2-d2]这段区间之中
// 如果在则说明存在一个回文日期
// 如果不在则说明不存在回文日期
int slove1(int year,int m1,int m2,int d1,int d2)
{
int tmp = year;
// 求出月份
int t1 = tmp%10; tmp /= 10;
t1 = t1*10+tmp%10; tmp /= 10;
// 求出具体的日期
int t2 = tmp%10; tmp /= 10;
t2 = t2*10+tmp%10; tmp /= 10;
// 判断日期是否合法
if(t1 > 12 || t1 == 0 || t2 == 0)
{
return 0;
}
else
{
// 对2月进行特判
if(t1 == 2)
{
// 非闰年有28天
int tmp = 28;
// 闰年有29天
if(ye(year))
tmp += 1;
// 判断日期是否超出本月天数上限
if(t2 > tmp)
return 0;
else
return 1;
}
else
{
// tmp表示t1月有多少天
int tmp = mon[t1];
// 如果日期不合法
if(t2 > tmp)
{
return 0;
}
else
{
// 判断是否在规定日期区间之内
if(t1 >= m1)
{
if(t1 > m2)
return 0;
else
{
// 判断是否在开始或者结束的那个月内
if(t1 == m1 || t1 == m2)
{
if(t1 == m1)
{
if(t2 >= d1)
return 1;
else
return 0;
}
else
{
if(t2 <= d2)
return 1;
else
return 0;
}
}
else
return 1;
}
}
else
return 0;
}
}
}
return 0;
}
// 枚举年份
int slove2(int start,int end)
{
int ans = 0;
// 计算这一整年内是否有回文
// 因此日期区间可以写[1月1日 - 12月31日]
for(int i = start;i <= end;i++)
ans += slove1(i,1,12,1,31);
return ans;
}
int main()
{
// date1 开始的日期
// date2 结束的日期
cin >> date1 >> date2;
if(date1 == date2) // 60%的数据
{
cout << check(date1) << endl;
}
else
{
// 在date1中提取出年份月份以及日期
y = date1/10000;
m = date1%10000/100;
d = date1%100;
// 在date2中提取出年份月份以及日期
y2 = date2/10000;
m2 = date2%10000/100;
d2 = date2%100;
// 如果是在同一年内
if(y == y2)
{
cout << slove1(y,m,m2,d,d2) << endl;
}
else
{
int ans = 0;
// 计算[y+1,y2-1]这个区间内共有多少个回文日期
ans += slove2(y+1,y2-1);
// 计算y年[m-d,12,31]这个日期区间内是否存在回文日期
ans += slove1(y,m,12,d,31);
// 计算y2年[1-1,m2-d2]这个日期区间内是否存在回文日期
ans += slove1(y2,1,m2,1,d2);
cout << ans << endl;
}
}
return 0;
}