【题目链接】
ybt 1974:【16NOIP普及组】回文日期
洛谷 P2010 [NOIP2016 普及组] 回文日期
【题目考点】
1. 模拟
【解题思路】
解法1:枚举 日期结构体
设表示日期的结构体,属性包括年月日,方法有:进入下一天,以及判断当前日期是否是回文的。
输入是日期是字符串,构造函数中将该字符串前4个字符变为数值,即为年。中间两个字符变为月,最后两个字符变为日。年月日是整型变量,此处可以使用substr()
函数取子串,以及stoi()
函数将字符串转为整数。
设让当前日期变为下一天的函数next()
,先让日加1,如果日超过本月天数,那么月份加1,日变为1。如果月份超过12,那么年份加1,月份变为1。如果月份变化,更新本月天数md。
判断当前日期是否是回文的,可以将年月日进行数字拆分,每一位保存在长为8的整型数组中。遍历数组的一半(<n/2),判断这个数组前后对应位置是否相同。如果相同,那么这个日期就是回文的。
主函数中,让d为起始日期,每次循环判断当前日期是否是回文的,如果是则计数,而后让日期变为下一天。如果当前日期是终止日期,那么跳出循环。
最后输出计数。
复杂度为两个日期数值的差值,极端情况下,从0年1月1日到9999年12月12日,一共有约
366
∗
10000
=
3
∗
1
0
6
366*10000=3*10^6
366∗10000=3∗106天,是可以接受的。
解法2:深搜 构造回文串
用整型数组保存数字,深搜构造所有可能的回文数字。构造出回文数字后,判断该日期是否合法,以及该日期是否在起始与终止日期之间。
最差情况下,只需要搜索4位数字,每位数字有10种,最大搜索次数为
1
0
4
10^4
104次,优于解法1。
【题解代码】
解法1:枚举 日期结构体
#include<bits/stdc++.h>
using namespace std;
struct Date//日期结构体
{
int y, m, d, md;//y:年, m:月, d:日, md:当前月份天数
Date(string s)//以字符串初始化日期
{
y = stoi(s.substr(0, 4));
m = stoi(s.substr(4, 2));
d = stoi(s.substr(6));
md = getMonthDay();
}
bool isLeap()//判断当年是否是闰年
{
return y % 400 == 0 || y % 100 != 0 && y % 4 == 0;
}
int getMonthDay()//获取当前月份的天数
{
if(m == 2)
return isLeap() ? 29 : 28;
else if(m == 4 || m == 6 || m == 9 || m == 11)
return 30;
else
return 31;
}
void next()//日期变为下一天
{
d++;
if(d > md)//如果日超出当月总天数
{
d = 1;//日设为1
m++;//月份加1
if(m > 12)//如果月份超出12
{
y++;
m = 1;
}
md = getMonthDay();
}
}
bool isHuiwen()//s:将当前日期转为字符串
{
int a[10] = {y/1000, y/100%10, y/10%10, y%10, m/10, m%10, d/10, d%10};
for(int i = 0; i < 4; ++i)
if(a[i] != a[7-i])
return false;
return true;
}
bool isSame(Date b)
{
return y == b.y && m == b.m && d == b.d;
}
};
int main()
{
string s1, s2;
int ct = 0;//ct:计数
cin >> s1 >> s2;//s1:起始日期字符串 s2:终止日期字符串
Date d(s1), d2(s2);
while(true)
{
if(d.isHuiwen())
ct++;
if(d.isSame(d2))
break;
d.next();
}
cout << ct << endl;
return 0;
}
解法2:深搜 构造回文串
#include<bits/stdc++.h>
using namespace std;
#define N 8
int d[N], d1[N], d2[N], ct;
bool isLater(int a[], int b[])//日期b是否与日期a相同,或在日期b的时间后面
{
int y_a = a[0]*1000+a[1]*100+a[2]*10+a[3], m_a = a[4]*10+a[5], d_a = a[6]*10+a[7];
int y_b = b[0]*1000+b[1]*100+b[2]*10+b[3], m_b = b[4]*10+b[5], d_b = b[6]*10+b[7];
if(y_b < y_a)
return false;
else if(y_b > y_a)
return true;
else
{
if(m_b < m_a)
return false;
else if(m_b > m_a)
return true;
else
return d_b >= d_a;
}
}
bool isValidDate(int a[])//日期a是否是合法的
{
int md[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
int year = a[0]*1000+a[1]*100+a[2]*10+a[3], month = a[4]*10+a[5], day = a[6]*10+a[7];
if(month > 12 || month < 1)
return false;
if(year % 400 == 0 || year % 100 != 0 && year % 4 == 0)//根据是否是闰年改变2月天数
md[2] = 29;
else
md[2] = 28;
if(day > md[month] || day < 1)//如果日子超过当月的天数,则不合法
return false;
return true;
}
void dfs(int k)//将d[k]与d[7-k]赋值
{
if(k == 2)
{//d[0],d[1]与d[6],d[7]都赋值完成,看看d[6]d[7]是不是合法日期
int day = d[6]*10+d[7];
if(day > 31 || day < 1)//日期最大为31
return;
}
if(k == 4)//如果完成所有位置的赋值
{
if(isValidDate(d) && isLater(d1, d) && isLater(d, d2))//d是合法日期,且在d1~d2之间
ct++;//计数
return;
}
for(int i = 0; i <= 9; ++i)
{
d[k] = d[7-k] = i;//前后对应位置都为i
dfs(k+1);//看下一对位置
}
}
void toNum(int a[], string s)
{
for(int i = 0; i < s.length(); ++i)
a[i] = s[i] - '0';
}
int main()
{
string s1, s2;
cin >> s1 >> s2;//s1:起始日期字符串 s2:终止日期字符串
toNum(d1, s1);
toNum(d2, s2);
dfs(0);
cout << ct << endl;
return 0;
}