LuoguP5440 - 【XR-2】奇迹

【LuoguP5440】【XR-2】奇迹
题目链接: https://www.luogu.org/problemnew/show/P5440

前言

不要学那些很酷很炫的算法,不要看不起那些基础算法,比如说搜索。

然后我就天天做搜索,最后退役。

分析

正解搜索或暴力枚举,代码区已经几乎是相同的思路,只是有些大佬有很多优化,效率快,但是特批有很多并且极其复杂。经对比我觉得我的代码可视性还是不错的,并且没有乱七八糟的特判,可以说是常规DFS代码+筛质数优化的代码。

下面说一下思路。首先题目很好理解,一个有效日期,日、月+日、年+月+日组成的数字均为质数,那就先不考虑优化,直接上模拟。

按DFS的常规模板(自己总结出来的),先有一个参数,代表搜索到哪一位。在本题中就是日期的第几位数字,我选择的顺序为:日->月->年,这样可以逐层判断,而且效率较高代码优雅。

第二个参数,是为了方便和小幅提升效率,我们把已经枚举完的位编成一个整数$ x $,然后日就是 x % 100 ,月就是 x % 10000 / 100 ,年就是x / 10000

这道题中还需要考虑到特殊情况,是否必须为闰年或大月。我为了方便,当成两个参数来传递,现在想想好像可以改一个。因为一个只考虑年份,一个只考虑月份。DFS带详细注释代码如下,自己觉得算得上优雅。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// 调用方法: dfs(8, 0, 0, 0)
int dfs(int nown, int num, int rn, int jy) {
if(nown == 0) {
// 日期全部枚举完
if(num / 10000 == 0) return 0;
// 非常关键,0不能当年份(我一开始没加就得10分)
if(rn && pdrn(num / 10000) == 0) return 0;
// 如果必须为闰年就判断年份
return pdzs(num);
// 总日期还得为质数
}
if(nown == 6) {
// 枚举完日
if(num == 0 || num > 31 || !pdzs(num)) return 0;
// 如果是等于0日、31日以上或不是质数就return
if(num == 31) jy= 1;
// 如果是31日就必须为大月
}
if(nown == 4) {
// 判断完日、月
if(num < 32 || num > 1231 || !pdzs(num)) return 0;
// 如果是等于0月、13月以上或不是质数就return
if(jy && !yue[num / 100]) return 0;
// 如果必须为大月就判断月份num / 100,yue数组代表是否为大月
if(num / 100 == 2) {
if(num % 100 > 29) return 0;
// 2月最多29天
if(num % 100 == 29) rn= 1;
// 如果是2月29日就必须为闰年
}
}
if(a[nown] != -1) return dfs(nown - 1, a[nown] * p10[8 - nown] + num, rn, jy);
// 如果输入给出就直接进入下一层,p10数组相当于pow10
int res= 0;
for(int i= 0; i <= 9; i++) res+= dfs(nown - 1, i * p10[8 - nown] + num, rn, jy);
// 枚举0~9为此位
return res;
}

如果是用复杂度为$ O(\sqrt{n}) $ 的判断质数就只有90分,所以还得用线性筛预处理出$ \sqrt{100000000} = 10000 $ 以内的质数,然后我们判断质数就对这些数取模就行了,具体原理不再解释。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 筛10005以内的质数
inline void init() {
flag[1]= 1;
for(int i= 2; i < 10005; i++) {
if(!flag[i]) prim[++tot]= i;
for(int j= 1; j <= tot; j++) {
if(i * prim[j] >= 10005) break;
flag[i * prim[j]]= 1;
if(i % prim[j] == 0) break;
}
}
return;
}
// 判断质数
inline int pdzs(int x) {
if(x < 2) return 0;
for(int i= 1; i <= tot; i++)
if(x % prim[i] == 0) return x == prim[i];
return 1;
}

然后就可以愉快的AC了,总代码只有70行且简单优雅。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include <iostream>
#include <stdio.h>
#include <math.h>
using namespace std;
int n, a[9], prim[10005], flag[10005], tot;
char tmpc;
int p10[]= {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000};
int yue[]= {0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1};
inline void init() {
flag[1]= 1;
for(int i= 2; i < 10005; i++) {
if(!flag[i]) prim[++tot]= i;
for(int j= 1; j <= tot; j++) {
if(i * prim[j] >= 10005) break;
flag[i * prim[j]]= 1;
if(i % prim[j] == 0) break;
}
}
return;
}
inline int pdrn(int x) {
return (x % 4 == 0 && x % 100 != 0) || (x % 400 == 0 && x % 3200 != 0);
}
inline int pdzs(int x) {
if(x < 2) return 0;
for(int i= 1; i <= tot; i++)
if(x % prim[i] == 0) return x == prim[i];
return 1;
}
int dfs(int nown, int num, int rn, int jy) {
if(nown == 0) {
if(num / 10000 == 0) return 0;
if(rn && pdrn(num / 10000) == 0) return 0;
return pdzs(num);
}
if(nown == 6) {
if(num == 0 || num > 31 || !pdzs(num)) return 0;
if(num == 31) jy= 1;
}
if(nown == 4) {
if(num < 32 || num > 1231 || !pdzs(num)) return 0;
if(jy && !yue[num / 100]) return 0;
if(num / 100 == 2) {
if(num % 100 > 29) return 0;
if(num % 100 == 29) rn= 1;
}
}
if(a[nown] != -1) return dfs(nown - 1, a[nown] * p10[8 - nown] + num, rn, jy);
int res= 0;
for(int i= 0; i <= 9; i++) res+= dfs(nown - 1, i * p10[8 - nown] + num, rn, jy);
return res;
}
char get() {
char ch= getchar();
while((ch < '0' || ch > '9') && ch != '-') ch= getchar();
return ch;
}
void put(int x) {
if(x > 9) put(x / 10);
putchar('0' + x % 10);
return;
}
int main() {
init(), cin >> n;
while(n--) {
for(int i= 1; i <= 8; i++) tmpc= get(), a[i]= (tmpc == '-' ? -1 : tmpc - '0');
put(dfs(8, 0, 0, 0)), putchar('\n');
}
return 0;
}

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×