2016 CCPC changchun(J)
题意:给你一个数字n,把n拆成不超过50个回文数字,使得拆成的回文数字之和等于n。(n可达到10的1000次方)
思路:
要求最多拆成50个数字,意味着这题必须去考虑如何拆能够尽量得到一个比较大的回文数字,这样能尽快把n变小。
方法是:对于一个数字,设长度为len,则取前半段(长度为len/2)的值减去 1 ,再翻转 , 那么就能得到一个小于 n 的最大回文数字 。(详见代码
坑点在于前导0的处理,以及字符串的一系列骚操作…
下面是代码
#include <bits/stdc++.h>
using namespace std;
string s;
vector<string> st;
string sub(string s,string tmp)//自定义减法运算,返回 s - tmp 的字符串
{
int i=s.length()-1,j=tmp.length()-1,k;
for(;j>=0;j--,i--)
{
if(s[i]<tmp[j])
{
s[i] = (s[i] + 10) - tmp[j] + '0';
s[i-1]--;
}
else s[i] = s[i] - tmp[j] + '0';
}
for(;i>=0;i--)//因为 s 串的数值一定大于 tmp ,直接把小于‘0’的退位补足
{
if(s[i]<'0')
{
s[i]+=10;
s[i-1]--;
}
}
string tmp2 ;
for(k=0;s[k]=='0';k++);//这一步是去除前导0
tmp2 = s.substr(k);
return tmp2;//返回s - tmp 的值(字符串形式)
}
void init()
{
string tmp,tmp2;
while(1)
{
if(s.length()<2 ||(s.length()==2&&s[0]=='1' ))break;
//结束标志为数字范围在[0,19]之间,不再适用于下面的操作,需要特判
tmp.clear();
tmp2.clear();
int len = s.length();
if(len%2)
{
tmp+=s.substr(0,(len+1)/2);//取出前半段的数字
tmp = sub(tmp,"1");//前半段数字减1
tmp2 = tmp;
reverse(tmp.begin(),tmp.end());//翻转
tmp2 += tmp.substr((((len+1)/2)>tmp2.length())?0:1);
st.push_back(tmp2);//接在一起就是回文数字,放进vector
s = sub(s,tmp2);//更新当前n的值
}
else(同上)
{
tmp+=s.substr(0,len/2);
tmp = sub(tmp,"1");
tmp2 = tmp;
reverse(tmp.begin(),tmp.end());
tmp2 += tmp;
st.push_back(tmp2);
s = sub(s,tmp2);
}
}
//下面全是特判
if(s.length()==2)
{
if(s[1]=='1')
{
st.push_back(s);
return;
}
else if(s[1]>s[0])
{
st.push_back("11");
s = sub(s,"11");
st.push_back(s);
}
else
{
st.push_back("1");
st.push_back("9");
}
}
else if(s.length()==1)
{
if(s[0]!='0')
st.push_back(s);
}
}
int main()
{
int t,ca=1;cin>>t;
while(t--)
{
cin>>s;
printf("Case #%d:\n",ca++);
st.clear();
init();
cout<<st.size()<<endl;
for(auto ss:st)
cout<<ss<<endl;
}
return 0;
}
这题只要想到如何把串的数量控制在50以内其实还是挺容易想到的,只是模拟写起来很累,需要非常细心。
转载请注明出处^ ^