1. 程式人生 > 實用技巧 >2016CCPC長春站 J - Ugly Problem (構造、貪心)

2016CCPC長春站 J - Ugly Problem (構造、貪心)

HDU 5920 - Ugly Problem


題意

將一個長度不超過\(1000\)的大整數拆分成不超過\(50\)個迴文數之和。



思路

每次考慮從數\(a\)中拆分出一個最大的迴文數\(b\)


高位開始向中位迴圈,把\(b\)中對稱的一對位置的數字定作數\(a\)中對應位置的數字

例如\(a=54321\),每個對稱位置取大,得到初始的\(b=54345\)

例如\(a=1716999\),得到\(b=1716171\)


然後判斷兩個數的大小,如果滿足\(a\leq b\),那麼\(b\)就可以作為這個迴文數,同時將\(a\)減去\(b\)

如果\(a\gt b\),那麼需要從數\(b\)

的中部考慮將某個數位減去\(1\)

因為數字\(a\)\(b\)的左半部分相同,如果\(b\)在左半部分任意減去一個\(1\)(假如能減),肯定可以保證\(a\lt b\)成立

所以從中位開始向高位迴圈,如果當前位不為\(0\)且不是最高位(會出現前導\(0\)),則將當前位與對稱的位同時減\(1\)(保證迴文);或者如果當前位是最高位,且大於\(1\),也可以減去

例如在\(a=54321\)的情況,初步得到\(b=54345\),發現中位\(3\ge 0\),故最終\(b=54245\)

例如在\(a=11000\)的情況,初步得到\(b=11011\),發現次高位\(1\ge 0\),故最終\(b=10001\)


但如果在最高位為\(1\),其餘位均為\(0\)的情況下(即樣例),那麼可以發現最大的迴文數為位數\(-1\)後全為\(9\)的數字

例如在\(a=10000\)的情況下,初步得到\(b=10001\),發現沒有任何一位可以被減,故最終\(b=9999\)


最後,手寫下大數減法、大數判斷即可。



程式

程式碼裡將數字倒著存了,然後用了變數\(len\)來控制當前處理的數字\(a\)的位數

每次進行減法之後就更新一次\(len\)

#include<bits/stdc++.h>
using namespace std;

int a[1050],b[1050];
int len;
vector<int> v[55];

bool check() //判斷a是否迴文
{
    for(int i=0,j=len-1;i<=j;i++,j--)
        if(a[i]!=a[j])
            return false;
    return true;
}

bool check2() //判斷a>=b
{
    for(int i=len-1;i>=0;i--)
        if(b[i]>a[i])
            return false;
        else if(b[i]<a[i])
            return true;
    return true;
}

void subtract() //將a=a-b,並更新len
{
    for(int i=0;i<len;i++)
    {
        if(a[i]<b[i])
        {
            a[i]+=10;
            a[i+1]-=1;
        }
        a[i]-=b[i];
    }
    while(len>0&&a[len-1]==0)
        len--;
}

void deal(int pos)
{
    if(check()) //如果是迴文,直接結束
    {
        v[pos].clear();
        for(int i=len-1;i>=0;i--)
            v[pos].push_back(a[i]);
        len=0;
        return;
    }
    
    for(int i=0,j=len-1;i<=j;i++,j--) //初步處理出數字b
    {
        b[i]=b[j]=a[j];
    }
    
    if(check2()) //如果a>=b,直接將b作為答案並a=a-b
    {
        v[pos].clear();
        int i=len-1;
        while(i>0&&b[i]==0)
            i--;
        for(;i>=0;i--)
            v[pos].push_back(b[i]);
        subtract();
    }
    else
    {
        bool flag=false;
        for(int i=len/2,j=len/2-(len%2==0?1:0);j>=0;i++,j--)
        {
            if(b[i]>0&&j>0||b[i]>1&&j==0) //嘗試做某一位的減法
            {
                b[i]--;
                if(i!=j)
                    b[j]--;
                flag=true;
                break;
            }
        }
        if(flag) //做了減法,此時可以直接a=a-b
        {
            v[pos].clear();
            int i=len-1;
            while(i>0&&b[i]==0)
                i--;
            for(;i>=0;i--)
                v[pos].push_back(b[i]);
            subtract();
        }
        else //否則,將b作為a位數-1後全為9的數
        {
            v[pos].clear();
            for(int i=0;i<len-1;i++)
                b[i]=9,v[pos].push_back(9);
            b[len-1]=0;
            subtract();
        }
    }
}

void solve(int cas)
{
    string str;
    cin>>str;
    len=str.size();
    for(int i=0;i<len;i++)
        a[i]=str[len-i-1]-'0';
    
    int t=0;
    while(len>0) //如果a的長度不為0,繼續處理下去
        deal(t++);
    
    cout<<t<<'\n';
    for(int i=0;i<t;i++)
    {
        for(int it:v[i])
            cout<<it;
        cout<<'\n';
    }
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    int T;cin>>T;
    for(int t=1;t<=T;t++)
    {
        cout<<"Case #"<<t<<":\n";
        solve(t);
    }
    return 0;
}