要求把一个数分成不超过50个回文数的和。
其实每次都找一个尽量接近当前数值的回文数,可以证明数的数量大概会接近 log(数的长度)个。至于怎么找,我是先构造一个回文数,假设当前剩下的值为a,我把a的前一半翻折到后一半去,那么就得到一个回文数,但是这个回文数可能会比a要打,然后我从中间位置到到第一位进行查询,一但找到一个数大于0, 那么我就把这个数减一,并且把中间位置到这个数的所有数都赋值为9,当然对称位置也要做相应的更改,于是就构造出一个最接近a的回文数b,用a-b再继续构造就行了。
发现这种构造完之后a-b的长度会接近于a的一半,所以数的个数大约是log级别的。
还要注意一下,对于类似1000这样的数,按我的方法会构造出0990,但实际上这不是一个回文数,所以对于这种情况要把这个数变成999。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
const int maxn = 2000;
char s[maxn];
int a[maxn], f[60][maxn], n, len, l, r;
int main() {
int tt;
scanf("%d", &tt);
for (int cases = 1; cases <= tt; cases++) {
scanf("%s", s);
memset(a, 0, sizeof(a));
len = strlen(s);
for (int i = len-1; i >= 0; i--) {
a[++a[0]] = s[i]-'0';
}
memset(f, 0, sizeof(f));
n = 0;
while (1) {
n++;
if (a[0] == 1) {
f[n][0] = a[0];
f[n][1] = a[1];
break;
}
r = a[0]; l = 1;
while (1) {
f[n][r] = a[r];
f[n][l] = f[n][r];
if (l == r || l+1 == r) break;
l++; r--;
}
f[n][0] = a[0];
bool flag = true;
for (int i = a[0]; i >= 1; i--) {
if (f[n][i] > a[i]) flag = false;
if (f[n][i] != a[i]) break;
}
if (!flag) {
while (1) {
if (f[n][l] > 0) {
f[n][l]--;
f[n][r] = f[n][l];
break;
} else {
f[n][l] = 9;
f[n][r] = 9;
l--; r++;
}
}
if (l == 1 && f[n][l] == 0) f[n][l] = 9;
while (f[n][0] > 1 && f[n][f[n][0]] == 0) f[n][0]--;
}
for (int i = 1; i <= a[0]; i++) {
if (a[i] < f[n][i]) {
a[i] += 10;
a[i+1]--;
}
a[i] = a[i]-f[n][i];
}
while (a[0] > 1 && a[a[0]] == 0) a[0]--;
if (a[0] == 1 && a[1] == 0) break;
}
printf("Case #%d:\n", cases);
printf("%d\n", n);
for (int i = 1; i <= n; i++) {
for (int j = f[i][0]; j >= 1; j--) printf("%d", f[i][j]);
printf("\n");
}
}
}