Ugly Problem

回文数分解算法

这里写图片描述



要求把一个数分成不超过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");
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值