2016CCPC長春站 J - 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\)
因為數字\(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; }