HDU - 5920 Ugly Problem 大数减法模拟+贪心

1.题意:给你一个数字N,让你把它拆成n(n<=50)个回文数字的和,并输出这些回文数字(N<=10^1000)

2.分析:

(1)对于一个数字,我们先找尽可能接近他的会回文数字,然后减去。但是这个回文数字太难找了,怎么办?我们可以把当前数字前半部分对称到后面去,比如123 其对应的会回文数字为121。但是这里就面临一个问题:120对应的121比120大,所以我们这里可以把12减1再对称为111就可以了

(2)为了防止冗杂的判断,我们为了保证回文 + 小于原数字,直接取前一半减1在对称。如10000->取前一半100->减99->对称9999     不断取剩余数字的回文,知道原数字减为0即可。

(3)特判:13取前一半减1对称后变成了00,也就是如果减1后变为了0,这样的只有1,所以保持1不动即可。还有10这个数字,保持不动对称为11,减1为00,都不符合,所以特判为 9 + 1;

3.代码:

#include <iostream>
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1000 + 7;
char num[maxn],num2[maxn],another[maxn],sub[maxn];
char ans[51][maxn];
char one[2] = "1";
bool judge(char *s){//判断当前数字是否为回文数字
   int lens = strlen(s);
   for(int i = 0;i<lens/2;i++){
        if(s[i]!=s[lens - i - 1]){
            return false;
        }
   }
   return true;
}
void Decrease(char *s1,char *s2){//大数减法,s1 - s2并把结果赋值给s1去掉前导0
    int len1 = strlen(s1);
    int len2 = strlen(s2);
    int i = len1-1;
    int j = len2-1;
    int flag = 0;
    while(i>=0&&j>=0){
        if(s1[i] - '0'>=flag){
            s1[i] = s1[i] - flag;
            flag = 0;
        }
        else{
            s1[i] = s1[i]  + 10 - flag;
            flag = 1;
        }
        if(s1[i]>=s2[j]){s1[i] = s1[i] -s2[j] + '0';}
        else{
            s1[i] = s1[i] + 10 - s2[j] + '0';
            flag = 1;
        }
        i--;
        j--;
    }
    while(flag&&i>=0){
        if(s1[i] - '0'>=flag){
            s1[i] = s1[i] - flag;
            flag = 0;
        }
        else{
            s1[i] = s1[i]  + 10 - flag;
            flag = 1;
        }
        i--;
    }
    //cout<<s1<<endl;
    int l = 0;
    bool flagd = false;
    memset(another,0,sizeof(another));
    for(int k = 0;k<len1;k++){//去掉前导0
        if(s1[k]=='0'&&!flagd)continue;
        else if(s1[k]!='0'&&!flagd){
            flagd = true;
            another[l++] = s1[k];
        }
        else if(flagd){
            another[l++] = s1[k];
        }
    }
    if(!l){another[l] = '0';l++;}//减法完为0
    another[l] = '\0';
    strcpy(s1,another);//复制
}
void palindromic(char *s1,char *s2){//取s1的回文串s2
    int len1 = strlen(s1);
    if(len1==2&&s1[0]=='1'&&s1[1]=='0'){//特判
        s2[0] = '9';
        s2[1] = '\0';
        return;
    }
    int len2 = (len1&1)?len1/2 + 1:len1/2;
    for(int i = 0;i<len2;i++){//取前一半
        s2[i]  = s1[i];
    }
    s2[len2] = '\0';
    Decrease(s2,one);//减1
    if(s2[0]=='0'){//判断1
        s2[0] = '1';
    }
    for(int i = len1-1,j = 0;j<i;i--,j++){
        s2[i] = s2[j];//对称
    }
    s2[len1] = '\0';
}
int main()
{
    int T;
    scanf("%d",&T);
    int t = 0;
    while(T--){
        scanf("%s",num);
        t++;
        int le = 0;
        while(num[0]!='0'&&le<=50){
            le++;
            //cout<<"num:"<<num<<endl;
            if(judge(num)){//当前为回文,直接结束
                strcpy(ans[le++],num);
                break;
            }
            memset(sub,0,sizeof(sub));
            palindromic(num,sub);//取回文数字sub
            //cout<<"sub:"<<sub<<endl;
            strcpy(ans[le++],sub);//保存结果sub
            Decrease(num,sub);//num - sub
        }
        printf("Case #%d:\n%d\n",t,le);
        for(int i = 0;i<le;i++){
            printf("%s\n",ans[i]);
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_40772692/article/details/82713166