SGU 108 Self-numbers II
类型:递推,生成。
思路:看网上比较多的一种解法是根据定义进行筛选,用压缩数组进行优化。我的解法源自对大量数据的分析,刚开始只发现前后增加了11,然后发现比实际多出了很多数,准确的是多出的9999个,又编写程序查找出了几个新的增量。并进一步发现了新的规律。
解法:
从第六个self number开始,d[i] = d[i-1]+11, 即增加量是11;
每出现9个11,就会出现一个2;
每出现9个2,就会出现一个15;
每出现9个15,就会出现一个28;
每出现9个28,就会出现一个41;
每出现9个41,就会出现一个54;
...
(据推测,往后还会出现新的增量,在10^7内有这几个增量)
需要做的调整:跟2相邻的前后各有8个11,而不是9个;
跟15相邻的有7个,28有6个...
补:
东山同学发现了增量之间也有规律,除去11。
2,15,28,41,54形成等差数列,差值是13。
那么下一个增量就是67,经验证是正确的。
但是,新的问题是:每当新的增量出现,就要跟着调整11的个数,67前后有3个11。
直到有1个11,那么可能出现的情况是什么呢?会不会是又开始9个11?(有待验证)
代码
/*
46 ms 3879 kb
key: 因为没有及时跳出,导致求错总个数。
*/
#include <stdio.h>
#include <string.h>
#define NL 1000011
int d[NL];
int main()
{
int n, k, i, mj, t, t1, t2, t3, t4;
int kt[5001];
// freopen("in.txt", "r", stdin);
// freopen("all.txt", "w", stdout);
scanf("%d%d", &n, &k);
mj = 0;
for (i=1; i<=k; i++) {
scanf("%d", &kt[i]);
if (kt[i] > mj) mj = kt[i];
}
d[1] = 1;
d[2] = 3;
d[3] = 5;
d[4] = 7;
d[5] = 9;
t = 1;
t1 = t2 = t3 = t4 = 0;
for (i=6; i<=n; i++) {
if (t == 10) {
t = 1;
t1++;
if (t1 == 9) {
if (t2 == 9) {
if (t3 == 9) {
if (t4 == 9) t++;
t++;
}
t++;
}
t++;
}
if (t1 == 10) {
t1 = 0;
t2++;
if (t2 == 10) {
t2 = 0;
t3++;
if (t3 == 10) {
t3 = 0;
t4++;
if (t4 == 10) {
t4 = 0;
d[i] = d[i-1]+54;
if (d[i] > n) break; //key
t = 5;
continue;
}
d[i] = d[i-1]+41;
if (d[i] > n) break; //key
t = 4;
continue;
}
d[i] = d[i-1]+28;
if (d[i] > n) break; //key
t = 3;
continue;
}
d[i] = d[i-1]+15;
if (d[i] > n) break; //key
t = 2;
continue;
}
d[i] = d[i-1]+2;
if (d[i] > n) break; //key
continue;
}
d[i] = d[i-1]+11;
t++;
if (d[i] > n) break;
}
if (n < 10) printf("%d\n", (n+1)/2);
else printf("%d\n", i-1);
for (i=1; i<=k; i++) {
if (i!=1) putchar(' ');
printf("%d", d[kt[i]]);
}
printf("\n");
return 0;
}