蓝桥杯真题:回文数字 https://www.lanqiao.cn/problems/498/learning/
解题思路:
首先看怎么得到一个回文日期——
我们随便打四个数字2252,如果要得到一个回文数字需要什么?
一个对称轴,像一个镜子一样,然后对称过去:2252 | 2522
所以我们可以从样例的年份开始模拟,2020 | 0202,判断是否为回文数字,判断是不是ABAB型。
不符合就年份加一,2021 | 1202,直到得到我们想要的结果。
原本也是没有什么思路,但是模拟了一下人类思考的过程,是从年份开始模拟,然后回文对称过去,看得到的月份和日期是否合法。输出第一个合法的日期(最小),每次合法判断日期是否月和日是否相等(ABAB型)
只输出第一次合法日期的方法:引入一个变量t,当出现回文数字后加一,所以当变量t为0的时候,就是回文日期第一次出现的时候。
于是就有了接下来这个程序:
#include <iostream>
#include <cstdio>
using namespace std;
int month[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; //设定每个月的天数用于判断天数是否合法
int n, m, d;
int judge(int y) {
if((y % 100 != 0 && y % 4 == 0) || y % 400 == 0) return 29; //单独判断二月的天数
else return 28;
}
void out(int y, int m, int d) { //将日期完全输出
cout << y;
if(m < 10) cout << "0";
cout << m;
if(d < 10) cout << "0";
cout << d << endl;
}
bool check(int y, int m, int d) { //检查日期是否合法
if((m > 12 || m == 0) || d == 0) return false; //检查月是否合法(在1-12月内)
if((m == 2 && d <= judge(y)) || ((m != 2) && d <= month[m - 1])) //检查日是否合法(在1-当月天数内)
return true;
else return false;
}
int main() {
cin >> n;
int y = n / 10000, t = 0; //从当前年份开始,依次向后
for(int i = y + 1; i; i ++) {
m = i % 10 * 10 + (i % 100) / 10; //计算年份对应的月份和日期
d = i / 1000 + ((i % 1000) / 100) * 10;
if(check(i, m, d)) { //如果日期合法,就进行输出。
if(t == 0) out(i, m, d); //非ABAB型只用输出第一次的,之后的再判断是否为ABAB型。
if(m == d) { //当属于ABAB型之后,进行输出。
out(i, m, d);
break;
}
}
y ++; t ++;
}
return 0;
}
但进行提交之后,只对了两个样例。
于是我开始试各种数字,再这里发现了纰漏:
当输入样例非回文数,但该年份存在回文数时,例如:20200201,第二天20200202就是回文数,但我们的程序是从第二年开始的,所以遇见这种样例就会出错。那么这时就需要改进。
所以现在怎么改进呢?难道说从样例的日期开始向后枚举吗?那也太麻烦了,其实我们知道,这种情况是当前年份存在回文日期,所以我们只要判断样例的年份是否是包含回文日期的年份,然后判断当年回文日期的年份是不是在样例的日期之后就可以了。
代码如下:
#include <iostream>
#include <cstdio>
using namespace std;
int month[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
int n, m, d;
int judge(int y) {
if((y % 100 != 0 && y % 4 == 0) || y % 400 == 0) return 29;
else return 28;
}
void out(int y, int m, int d) {
cout << y;
if(m < 10) cout << "0";
cout << m;
if(d < 10) cout << "0";
cout << d << endl;
}
bool check(int y, int m, int d) {
if((m > 12 || m == 0) || d == 0) return false;
if((m == 2 && d <= judge(y)) || ((m != 2) && d <= month[m - 1]))
return true;
else return false;
}
int main() {
cin >> n;
int y = n / 10000, t = 0;
m = y % 10 * 10 + (y % 100) / 10;
d = y / 1000 + ((y % 1000) / 100) * 10;
///
//新增部分
int m1 = n % 10000 / 100; //计算样例的月份
int d1 = n % 100; //计算样例的日期
if(check(y, m, d) && (m > m1 || d > d1)) { //当前年份包含回文日期,并且该日期在样例之后
out(y, m, d); //输出该日期
if(m == d) { //若该日期刚好为回文数字则输出两次并直接结束程序
out(y, m, d);
return 0;
}
t ++; //如若此时没有结束程序,则表示样例年份的回文日期并非ABAB型,
} //那么就是第一个最小的回文数字,需要用变量标记防止第二个非ABAB型的回文数字输出
//
for(int i = y + 1; i; i ++) {
m = i % 10 * 10 + (i % 100) / 10;
d = i / 1000 + ((i % 1000) / 100) * 10;
if(check(i, m, d)) {
if(t == 0) out(i, m, d);
if(m == d) {
out(i, m, d);
break;
}
t ++;
}
}
return 0;
}