题目
#题目
【问题描述】
若一个数(首位不为0)从左向右读和从右向左读都是一样,我们就称其为回文数。例
如,给定一个十进制数56,将56+65(即把56从右向左读),得到的121是一个回文串。
又如,十进制整数87:
STEP1: 87+78=165
STEP2: 165+561=726
STEP3: 726+627=1353
STEP4: 1353+3531=4884
在这里,是进行一次N进制的加法,上例用了最少四次得到回文数4884
写一个程序,将N进制数m,m的尾数上限为20。求最少几步可以得到回文数,如果在30
步以内不能得到回文数,输出“impossible”。
【输入格式】
一行,有空格隔开的两个数,n,m(m为正整数)
【输出格式】
也是一行,最少得到回文数得步数,若30步以内,得不到回文数,输出“impossible
”。
【输入样例】
9 87
【输出样例】
6
分析
在n进制下(尾数上限为20,即最高的那一位数为20(K
)),输入一个数,将它不断地与自己反着读得的数相加,直到得数是回文数,最多循环30次,若得出,就输出“impossible
”。
解答
步骤详解
-
框架先写上
int main(){ return 0; }
-
首先,输入数据,为了方便循环赋值,m先用char数组输入。
为了方便后面进位,所以从后往前的把数据放入int数组。这也是不能直接循环输入到int数组的原因之一。数组下标 0 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 char数组 1 9 2 A 3 4 5 7 8 \0 0 0 0 0 0 0 0 int数组 0 0 0 0 0 0 0 0 1 9 2 A 3 4 5 7 8 如果进制(n)大于10,就会出现大写字母;要先判断再赋值。
#include<cstdio> //scanf() #include<cstring> //strlen() int n, m[1024]; char scan_m[1024]; int main(){ scanf("%d %s", &n, &scan_m); for(int i=0; i<strlen(scan_m); i++){ char n=scan_m[strlen(scan_m)-1-i]; if(n>='0' && n<='9'){ m[1023-i]=n-'0'; }else{ m[1023-i]=n-'A'+10; } } return 0; }
-
为了方便后续的操作,我们先写一个自定义函数求当前数组m的位数(不算上前面的
0
)#include<cstdio> //scanf() #include<cstring> //strlen() int n, m[1024]; char scan_m[1024]; int intlen(int n[]){ int len; for(len=0; n[len]==0; len++); return 1024-len; } int main(){ scanf("%d %s", &n, &scan_m); for(int i=0; i<strlen(scan_m); i++){ char n=scan_m[strlen(scan_m)-1-i]; if(n>='0' && n<='9'){ m[1023-i]=n-'0'; }else{ m[1023-i]=n-'A'+10; } } return 0; }
-
后面我们就要做加法了,现在我们先把循环30次写上。
#include<cstdio> //scanf() #include<cstring> //strlen() int n, m[1024], temp[1024]; char scan_m[1024]; bool hw; int intlen(int a[]){ int len; for(len=0; a[len]==0; len++); return 1024-len; } int main(){ scanf("%d %s", &n, &scan_m); for(int i=0; i<strlen(scan_m); i++){ char n=scan_m[strlen(scan_m)-1-i]; if(n>='0' && n<='9'){ m[1023-i]=n-'0'; }else{ m[1023-i]=n-'A'+10; } } for(int i=1; i<=30; i++){} return 0; }
-
接下来,开始做加法(和进位)。
其实第二个加数没有必要新建数组(得数还是要的),我们可以直接数组m的第(n+)i位加上数组m的第倒数第i位(下图的n
指该数组前面空出的0的数量,n=1024-intlen()
)。数组下标 n+0 n+1 n+2 n+3 n+4 加数1 3 5 2 8 1 加数2 1 8 2 5 3 十进制加法是逢十进一,n进制加法便是逢n进一。尽管n大于10也没关系。
#include<cstdio> //scanf() #include<cstring> //strlen() int n, m[1024], temp[1024]; char scan_m[1024]; int intlen(int a[]){ int len; for(len=0; a[len]==0; len++); return 1024-len; } int main(){ scanf("%d %s", &n, &scan_m); for(int i=0; i<strlen(scan_m); i++){ char n=scan_m[strlen(scan_m)-1-i]; if(n>='0' && n<='9'){ m[1023-i]=n-'0'; }else{ m[1023-i]=n-'A'+10; } } for(int i=1; i<=30; i++){ for(int i=0; i<intlen(m); i++){ temp[1024-intlen(m)+i]=m[1024-intlen(m)+i]+m[1023-i]; if (temp[1024-intlen(m)+i]>=n){ temp[1024-intlen(m)+i]-=n; temp[1024-intlen(m)+i-1]++; } } for(int i=0; i<intlen(temp); i++){ m[1024-intlen(temp)+i]=temp[1024-intlen(temp)+i]; } } return 0; }
-
现在,判读回文数就够了。如果是回文数,就输出当前次数,然后可以直接结束程序了;否则,如果大循环30了次后都没有得出回文数,就输出“
impossible
”。
老样子,直接判断数组m的第(n+)i位是否等于数组m的第倒数第i位即可,而且,为了保证速度,这里只需要遍历一半就够了。#include<cstdio> //scanf() #include<cstring> //strlen() int n, m[1024], temp[1024]; char scan_m[1024]; bool hw; int intlen(int a[]){ int len; for(len=0; a[len]==0; len++); return 1024-len; } int main(){ scanf("%d %s", &n, &scan_m); for(int i=0; i<strlen(scan_m); i++){ char n=scan_m[strlen(scan_m)-1-i]; if(n>='0' && n<='9'){ m[1023-i]=n-'0'; }else{ m[1023-i]=n-'A'+10; } } for(int i=1; i<=30; i++){ for(int i=0; i<intlen(m); i++){ temp[1024-intlen(m)+i]=m[1024-intlen(m)+i]+m[1023-i]; if(temp[1024-intlen(m)+i]>=n){ temp[1024-intlen(m)+i]-=n; temp[1024-intlen(m)+i-1]++; } } for(int i=0; i<intlen(temp); i++){ m[1024-intlen(temp)+i]=temp[1024-intlen(temp)+i]; } hw=true; for(int i=0; i<=intlen(m)/2; i++){ if(m[1024-intlen(m)+i]!=m[1024-1-i]){ hw=false; } } if(hw){ printf("%d", i); return 0; } } printf("impossible"); return 0; }
答案
#include<cstdio> //scanf()
#include<cstring> //strlen()
int n, m[1024], temp[1024];
char scan_m[1024];
bool hw;
int intlen(int a[]){
int len;
for(len=0; a[len]==0; len++);
return 1024-len;
}
int main(){
scanf("%d %s", &n, &scan_m);
for(int i=0; i<strlen(scan_m); i++){
char n=scan_m[strlen(scan_m)-1-i];
if(n>='0' && n<='9'){
m[1023-i]=n-'0';
}else{
m[1023-i]=n-'A'+10;
}
}
for(int i=1; i<=30; i++){
for(int i=0; i<intlen(m); i++){
temp[1024-intlen(m)+i]=m[1024-intlen(m)+i]+m[1023-i];
if(temp[1024-intlen(m)+i]>=n){
temp[1024-intlen(m)+i]-=n;
temp[1024-intlen(m)+i-1]++;
}
}
for(int i=0; i<intlen(temp); i++){
m[1024-intlen(temp)+i]=temp[1024-intlen(temp)+i];
}
hw=true;
for(int i=0; i<=intlen(m)/2; i++){
if(m[1024-intlen(m)+i]!=m[1024-1-i]){
hw=false;
}
}
if(hw){
printf("%d", i);
return 0;
}
}
printf("impossible");
return 0;
}
尾声(鸡汤)
看似很难操作的n进制加法其实理解了本质一点都不难,有没有用到特殊的数据类型。所以,我们做一件事要知其意,理解本质,才能活学活用。
附
大家还可以看看这道题《C++不使用公式和选择、循环求阶和》