最小回文数

/*
 *回文数
 *题目详情:
 *如果一个数正着读和反着读一样大,则这个数叫做回文数,例如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<stdio.h>
#include<stdlib.h>
#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-2+len/2){
        left++;
        right--;
        *right=*left=x[left-result];
        sum-=2*C2NUM(*left);
        if(sum<=0){
            sum+=2*C2NUM(*left);
            left--;
            right++;
            break;
        }
    }
    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<stdio.h>
#include<stdlib.h>
#include<memory.h>
#include<math.h>
#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)
      return 0;
    //和是奇数,位数为偶数,这样的条件下不存在解
    if(sum%2==1&&len%2==0)
      return 0;
    if(len==1){//位数为1时特别对待
        if(sum<10&&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-2+len/2){
        left++;
        right--;
        *right=*left=x[left-result];
        sum-=2*C2NUM(*left);
        if(sum<=0){
            sum+=2*C2NUM(*left);
            left--;
            right++;
            break;
        }
    }
//    printf("x=%s,sum=%d\n",x,sum);
    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 //提示:自动阅卷结束唯一标识,请勿删除或增加。

测试结果如下:


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值