/*
*回文数
*题目详情:
*如果一个数正着读和反着读一样大,则这个数叫做回文数,例如121是回文数,123454321是回文数。
*现给定一个正整数x,输出一个回文数y,要求y > x,并且组成x的所有数字之和与组成y的所有数字之和相等,以及y > x。
*x在10^1000以内,因为数字较大,我们用字符串作为输入和输出。
*如果无解,请输出Impossible。如果有多个y,输出最小的那个。
*例如:
*输入919,输出14941
*输入1,输出Impossible
*
*时间:2014-01-11
*原题:http://hero.csdn.net/OnlineCompiler/Index?ID=223&ExamID=218&ExamResultID=117157&ExamNum=2&random=1757529427
*/
#include
#include
#define NO_ANS "Impossible"//无解
#define C2NUM(c) ((c)-'0')//字符转十进制数,如'1'->1
#define N2CHAR(n) ((n)+'0')//十进制数字转字符,如1->'1'
//字符串长度
int strLen(const char*str){
char*tmp=(char*)str;
if(!str)
return 0;
while(*tmp)tmp++;
return tmp-str;
}
//比较两个数的大小
//strA>strB则返回正数,相等则返回0,否则返回负数
int cmpStr(const char*strA,const char*strB){
int lena,lenb;
//去除前面的数字0
while(*strA=='0')strA++;
while(*strB=='0')strB++;
if((!*strA)||(!*strB))//至少有一个数等于0
return *strA-*strB;
// printf("a=%s,b=%s\n",strA,strB);
lena=strLen(strA);
lenb=strLen(strB);
if(lena!=lenb)
return lena-lenb;
//长度相等时数字大小比较
while(*strA!=0&&*strA==*strB)
strA++,strB++;
// printf("a=%s,b=%s\n",strA,strB);
return *strA-*strB;
}
//作用:数字和=sum,位数=len,首位>=headMin的最小回文数
//返回:无法找到则返回0,否则返回非负数
int minHWA(int sum,int len,int headMin,char*buf){
char*left,*right;
//if(!sum&&!len)
//return 1;
if(len<=0||headMin<0||headMin>9||sum<2*headMin)
return 0;
//和是奇数,位数为偶数,这样的条件下不存在解
if(sum%2==1&&len%2==0)
return 0;
buf[0]=buf[len-1]=N2CHAR(headMin);//暂定首尾的数字为headMin
sum-=2*headMin;
left=buf+len/2-1;//回文数的左侧
right=left+1+len%2;//回文数的右侧
//长度为偶数
if(len%2==0){
sum/=2;
}else{
//确定中间的数字
*(left+1)=sum%2==0?
(sum>8?N2CHAR(8):N2CHAR(sum)):
(sum>9?N2CHAR(9):N2CHAR(sum));
sum=(sum-C2NUM(*(left+1)))/2;
}
if(sum+headMin>len/2*9)
return 0;
//从低位到高位依次确定各位的数字,优先选择最大的数
while(left>buf){
*left=sum>9?N2CHAR(9):N2CHAR(sum);//取尽可能大的数
*right=*left;//对称的数
sum-=C2NUM(*left);//左半边数字和减去相应的数字
left--;//更高位
right++;
}
*right=*left=*left+sum;//剩余和加到首位上
return 1;
}
//作用:数字和等于x,且大于x的最小回文数
//算法:首先考虑位数相等的时候,是否有解
//先求数字总和sum,如果sum是奇数,位数是偶数,则不存在等长的回文数满足条件
//设x=X1_X2,要寻找最小的回文数y,则y的高位要尽可能和x的高位相等
//设y=y1_y2_y3,y1=y3=x1,y2也是回文数
//先选择位数尽可能多的x1,则sum(y2)=sum(x)-2*sum(x1),显然要保证sum(y2)>=0
//y1=x1,y1选定的情况下,查看是否存在回文数y2满足条件,且y2_y3>x2
//若不存在,则x1减最低位,再检查是否存在y2满足条件
//若直到x1位数为0,也无法找到,则说明同等位数下,无法找到满足条件的回文数y
//计算位数比x位数多的最小回文数y,y的首尾都是1
char* palindrom (char* x)
{
int len,sum=0;
char*result,*left,*right;
len=strLen(x);
result=(char*)malloc((4+len)*sizeof(char));
left=x;
//求数字和
while(*left++)
sum+=C2NUM(*(left-1));
left=result-1;//result的前一位
right=left+len+1;//result的末尾
*(right+2)=*(right+1)=*right='\0';
if(sum%2==1&&len%2==0){//数字和为奇数,长度为偶数,大于此数的回文数长度必大1
// printf("x=%s,sum=%d,len=%d\n",a,sum,len+1);
return minHWA(sum,len+1,1,result)?result:NO_ANS;
}
while(left=result-1){
++left;//保证left之前的字符不变
--right;
*right=*left=x[left-result];
sum-=2*C2NUM(*left);
//*left和*right增到9,检查是否满足条件
while(*left<'9'&&sum>=0){
if(sum==0){
minHWA(0,right-left-1,0,left+1);//填充0
//result是回文数,如果大于a,则返回
if(cmpStr(left,left-result+x)>0)
return result;
}
*right=++*left;
sum-=2;
if(minHWA(sum,right-left-1,0,left+1))
return result;
}
sum+=2*C2NUM(*left);
if(left-1>=result)
sum+=2*C2NUM(*(left-1));
left-=2;
right+=2;
}
left=result;
//数字和是奇数,长度也是奇数,大于该数的最小回文数长度为奇数
if(sum%2==1&&len%2==1)
right=left+len+1;
else//位数大1的数里边有满足条件的回文数
right=left+len;
*(right+1)='\0';
// printf("sum=%d,len=%d\n",sum,right-left+1);
return minHWA(sum,right-left+1,1,left)?result:NO_ANS;
}
//整数转成字符串,测试用
char* num2Str(int n,char*buf){
char*tmp=buf,*str=buf;
char c;
while(n>9){
*tmp++=N2CHAR(n%10);
n/=10;
}
*tmp++=N2CHAR(n);
*tmp--='\0';
while(tmp>str){
c=*tmp;
*tmp=*str;
*str=c;
str++;
tmp--;
}
return buf;
}
//start 提示:自动阅卷起始唯一标识,请勿删除或增加。
int main()
{
char buf[1024]={0};
int k;
// printf("%s\n",palindrom("1"));
// printf("%s\n",palindrom("3"));
// printf("%s\n",palindrom("5"));
// printf("%s\n",palindrom("7"));
// printf("%s\n",palindrom("9"));
// printf("%s\n",palindrom("10"));
// printf("%s\n",palindrom("11"));
// printf("%s\n",palindrom("12"));
// printf("%s\n",palindrom("21"));
// printf("%s\n",palindrom("22"));
for(k=1;k<1000;k+=13){
num2Str(k,buf);
printf("%-4d %s\n",k,palindrom(buf));
}
// printf("%s\n",palindrom("293"));
// printf("%s\n",palindrom("200"));
// printf("%s\n",palindrom("999"));
// printf("%s\n",palindrom("9876"));
}
//end //提示:自动阅卷结束唯一标识,请勿删除或增加。
测试结果如下图所示:
上述算法有些bug,经过改进之后,代码如下:
/*
*回文数
*题目详情:
*如果一个数正着读和反着读一样大,则这个数叫做回文数,例如121是回文数,123454321是回文数。
*现给定一个正整数x,输出一个回文数y,要求y > x,并且组成x的所有数字之和与组成y的所有数字之和相等,以及y > x。
*x在10^1000以内,因为数字较大,我们用字符串作为输入和输出。
*如果无解,请输出Impossible。如果有多个y,输出最小的那个。
*例如:
*输入919,输出14941
*输入1,输出Impossible
*
*时间:2014-01-11
*原题:http://hero.csdn.net/OnlineCompiler/Index?ID=223&ExamID=218&ExamResultID=117157&ExamNum=2&random=1757529427
*/
#include
#include
#include
#include
#define NO_ANS "Impossible"//无解
#define C2NUM(c) ((c)-'0')//字符转十进制数,如'1'->1
#define N2CHAR(n) ((n)+'0')//十进制数字转字符,如1->'1'
//字符串长度
int strLen(const char*str){
char*tmp=(char*)str;
if(!str)
return 0;
while(*tmp)tmp++;
return tmp-str;
}
//比较两个数的大小
//strA>strB则返回正数,相等则返回0,否则返回负数
int cmpStr(const char*strA,const char*strB){
int lena,lenb;
//去除前面的数字0
while(*strA=='0')strA++;
while(*strB=='0')strB++;
if((!*strA)||(!*strB))//至少有一个数等于0
return *strA-*strB;
// printf("a=%s,b=%s\n",strA,strB);
lena=strLen(strA);
lenb=strLen(strB);
if(lena!=lenb)
return lena-lenb;
//长度相等时数字大小比较
while(*strA!=0&&*strA==*strB)
strA++,strB++;
// printf("a=%s,b=%s\n",strA,strB);
return *strA-*strB;
}
//作用:数字和=sum,位数=len,首位>=headMin的最小回文数
//返回:无法找到则返回0,否则返回非负数
int minHWA(int sum,int len,int headMin,char*buf){
char*left,*right;
if(len<=0||headMin<0||headMin>9||sum=headMin){
buf[0]=N2CHAR(sum);
return 1;
}
return 0;
}
if(sum<2*headMin||sum>9*len)
return 0;
buf[0]=buf[len-1]=N2CHAR(headMin);//暂定首尾的数字为headMin
sum-=2*headMin;
left=buf+len/2-1;//回文数的左侧
right=left+1+len%2;//回文数的右侧
//长度为偶数
if(len%2==0){
sum/=2;
}else{
//长度为奇数时先确定中间的数字
*(left+1)=sum%2==0?
(sum>8?N2CHAR(8):N2CHAR(sum)):
(sum>9?N2CHAR(9):N2CHAR(sum));
sum=(sum-C2NUM(*(left+1)))/2;
}
//从低位到高位依次确定各位的数字,优先选择最大的数
while(left>buf){
*left=sum>9?N2CHAR(9):N2CHAR(sum);//取尽可能大的数
*right=*left;//对称的数
sum-=C2NUM(*left);//左半边数字和减去相应的数字
left--;//更高位
right++;
}
*right=*left=*left+sum;//剩余和加到首位上
return 1;
}
//作用:数字和等于x,且大于x的最小回文数
//算法:首先考虑位数相等的时候,是否有解
//先求数字总和sum,如果sum是奇数,位数是偶数,则不存在等长的回文数满足条件
//设x=X1_X2,要寻找最小的回文数y,则y的高位要尽可能和x的高位相等
//设y=y1_y2_y3,y1=y3=x1,y2也是回文数
//先选择位数尽可能多的x1,则sum(y2)=sum(x)-2*sum(x1),显然要保证sum(y2)>=0
//y1=x1,y1选定的情况下,查看是否存在回文数y2满足条件,且y2_y3>x2
//若不存在,则x1减最低位,再检查是否存在y2满足条件
//若直到x1位数为0,也无法找到,则说明同等位数下,无法找到满足条件的回文数y
//计算位数比x位数多的最小回文数y,y的首尾都是1
char* palindrom (char* x)
{
int len,sum=0,r;
char*result,*left,*right;
len=strLen(x);
result=(char*)malloc((4+len)*sizeof(char));
memset(result,'0',(len)*sizeof(char));
memset(result+len,'\0',4*sizeof(char));
left=x;
//求数字和
while(*left++)
sum+=C2NUM(*(left-1));
if(sum<=1){//数字和小于2的,必无满足条件的回文数
free(result);
return NO_ANS;
}
left=result-1;//result的前一位
right=left+len+1;//result的末尾
if(sum%2==1&&len%2==0){//数字和是奇数
len+=1;
if(minHWA(sum,len,1,result))
return result;
free(result);
return NO_ANS;
}
while(left=result-1){
++left;//保证left之前的字符不变
--right;
*right=*left=x[left-result];
sum-=2*C2NUM(*left);
// printf("result=%s,sum=%d\n",result,sum);
//*left和*right增到9,检查是否存在满足条件的解
if(sum>0){
if(minHWA(sum,right-left-1,C2NUM(x[left-result+1]),left+1)
&&cmpStr(left,left-result+x)>0)
return result;
while(*left<'9'&&sum>=2){
*right=++*left;
sum-=2;
// printf("result=%s,sum=%d\n",result,sum);
if(minHWA(sum,right-left-1,0,left+1)||sum==0)
return result;
}
}else if(sum==0&&C2NUM(x[left-result+1])==0){
minHWA(sum,right-left-1,C2NUM(x[left-result+1]),left+1);
if(cmpStr(left,left-result+x)>0)
return result;
}
sum+=2*C2NUM(*left);
if(left-1>=result)
sum+=2*C2NUM(*(left-1));
left-=2;
right+=2;
}
left=result;
if(minHWA(sum,len+1,1,left))//位数大1时是否有解
return result;
if(minHWA(sum,len+2,1,left))//位数大2时是否有解
return result;
free(result);
return NO_ANS;//无解
}
//整数转成字符串,测试用
char* num2Str(int n,char*buf){
char*tmp=buf,*str=buf;
char c;
while(n>9){
*tmp++=N2CHAR(n%10);
n/=10;
}
*tmp++=N2CHAR(n);
*tmp--='\0';
while(tmp>str){
c=*tmp;
*tmp=*str;
*str=c;
str++;
tmp--;
}
return buf;
}
//测试,数x的最小回文数是ans
#define TEST(x,ans) printf("n=%-5d ans=%-12s assert="#ans"\n",(x),palindrom(#x))
//start 提示:自动阅卷起始唯一标识,请勿删除或增加。
int main()
{
char buf[1024]={0};
int k;
TEST(1,Impossible);
TEST(10,Impossible);
TEST(3,111);
TEST(101,1001);
TEST(2,11);
TEST(103,121);
TEST(11,101);
TEST(12,111);
TEST(13,22);
TEST(998,4994);
TEST(121,202);
TEST(131,212);
for(k=1;k<10000;k+=k%11+13){
num2Str(k,buf);
printf("%-4d %s\n",k,palindrom(buf));
}
buf[0]=N2CHAR(1+rand()%9);
for(k=1;k<1000;k++)
buf[k]=N2CHAR(rand()%10);
printf("x=%s\nresult=%s\n",buf,palindrom(buf));
// printf("%s\n",palindrom("293"));
// printf("%s\n",palindrom("200"));
// printf("%s\n",palindrom("999"));
// printf("%s\n",palindrom("9876"));
}
//end //提示:自动阅卷结束唯一标识,请勿删除或增加。
测试结果如下: