此题作为今年NOIP唯一一道略简单 普及— 难度的题,做法与某种强大的素数筛法(埃氏筛法)雷同,具体做法如下:
我们先来看题目:
如果下一个报的数是 7 的倍数,或十进制表示中含有数字 7,就必须跳过这个数。任何一个十进制中含有数字 7 的数,它的所有倍数都不能报出来。
我们可以发现,这和埃氏筛法的思想很像,其实就是模拟,我们可以用一个vis数组来记录一个数是否应该被跳过。
看代码:
void init () { for (int i = 1; i <= 10000000; i++) { if (!vis[i] && (seven(i) || i == 7)) { for (int j = 1; i * j <= inf; j++) { vis[i * j] = true; } } } }
其中的seven函数是用来判断这个数中是否包含7的
bool seven (int x) { while (x) { if (x % 10 == 7) return true; x /= 10; } return false; }
这样我们就预处理好了10^7以内的数是否应该被跳过
于是乎,我就交了一发这样的代码:
#include <iostream> using namespace std; int t, x; bool vis[10000010]; const int inf = 10000010; bool seven (int x) { while (x) { if (x % 10 == 7) { return true; } x /= 10; } return false; } void init () { for (int i = 1; i <= 10000000; i++) { if (!vis[i] && (seven(i) || i == 7)) { for (int j = 1; i * j <= inf; j++) { vis[i * j] = true; } } } } int main () { init(); scanf("%d", &t); while (t--) { scanf("%d", &x); if (vis[x]) printf("-1\n"); else { for (int i = x + 1; i; i++) { if (!vis[i]) { printf("%d\n", i); break; } } } } return 0; }
你以为这样就结束了吗?
非也,非也。
我荣幸地拿到了70分,T了三个点。
经过我详细的计算粗略的猜测,我觉得可能会多次询问同一个数,我就开始改进while循环中的代码。我想到了老师说过的记忆化搜索(用空间换时间玄学大法),就用f数组来记录已经询问过的数。
没想到真的就这样卡过去了。。。
附上AC代码
#include <iostream> using namespace std; int t, x; bool vis[10000010]; int f[10000010]; const int inf = 10000010; bool seven (int x) { while (x) { if (x % 10 == 7) return true; x /= 10; } return false; } void init () { for (int i = 1; i <= 10000000; i++) { if (!vis[i] && (seven(i) || i == 7)) { for (int j = 1; i * j <= inf; j++) { vis[i * j] = true; } } } } int main () { init(); scanf("%d", &t); while (t--) { scanf("%d", &x); if (f[x] != 0) { printf("%d\n", f[x]); continue; } if (vis[x]) printf("-1\n"); else { for (int i = x + 1; i; i++) { if (!vis[i]) { printf("%d\n", i); f[x] = i; break; } } } } return 0; }