题目描述:
若一个数(首位不为零)从左向右读与从右向左读都一样,我们就将其称之为回文数。
例如:给定一个10进制数56,将56加65(即把56从右向左读),得到121是一个回文数。
又如:对于10进制数87:
STEP1:87+78 = 165 STEP2:165+561 = 726
STEP3:726+627 = 1353 STEP4:1353+3531 = 4884
在这里的一步是指进行了一次N进制的加法,上例最少用了4步得到回文数4884。
写一个程序,给定一个N(2<=N<=10或N=16)进制数M(100位之内),求最少经过几步可以得到回文数。如果在30步以内(包含30步)不可能得到回文数,则输出“Impossible!”
进制N>10时,使用大写'A'字母表示10,'B'表示11,...,'E'表示16
输入描述:
两行,分别为N,M
输出描述:
STEP=ans
输入
9 87
输出
STEP=6
解题思路:
-
一维数组存储数据
-
回文数判断
-
加法运算处理
详细讲解:
对于数据存储这块,long long 的范围是-9223372036854775808~9223372036854775807,题目所述N进制的M在100位之内,很显然long long是放不下M的,考虑到后续需要实现加法运算和进位处理,所以此处使用一维数组的形式存储数据,每个数组放置一个位数,例如N = 9,M=123,a[0] = 1,a[1] = 2,a[2] = 3;
根据回文数的特点,回文数个数为奇数个时,除了最中间的那位数以外,第1位数与倒数1位数相等,第2位数与倒数第2位数相等...以此类推,我们只要使用两个指针i和j分别指向数组头和数组尾,逐个判断直至数组长度的一半即可。
加法运算处理这块稍微麻烦一点,在进行加法的过程中需要考虑不同进制的加法。那么在此处就需要读者需要不同进制加法的基础知识,例如,9进制下87+78的结果是多少?2进制下100+001的结果是多少?对于任何进制N下的数a和数b相加的结果范围在[0,2N-1],将他们相加的数使用if进行判断,大于等于N则需要进位,此处使用CF代表进位(CF == 1 则进位,CF == 0 则不需要进位),在下一位的两个数相加之前将CF加上即可。
注意:10~15的数表示成A~F,在输入时根据ASCII码进行处理转换。
AC代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MA = 1e7+7;
ll ans = 0,len;
ll N;
ll a[MA];//使用数组存储各个位数
//回文数判断
bool judge(){
ll i = 0,j = len - 1;
while(i != len/2){
if(a[i++] != a[j--])
return false;
}
return true;
}
//N进制加法处理
void addnumber(){
ans++;
ll i=0,j=len-1;
//按题目所述步骤加法得到每一位
while(i !=len/2){
ll num = a[i] + a[j];
a[i++] = num,a[j--] = num;
}
//防止奇数个数位
if(len % 2==1)
a[i] = 2*a[i];
//进位处理
ll CF = 0;
for(int k=0;k<len;k++){
ll nb;
//判断上一位是否需要进位
nb = a[k] + CF;
//该位数是否有进位
if(nb >= N)
CF = 1,nb -= N;
else
CF = 0;
//更新
a[k] = nb;
}
//判断最后一位是否需要进位
if(CF)
a[len++] = 1;
}
int main(){
cin >> N;
string S;
cin >> S;
//将数字均放入a数组中
for(int i=0;i<S.length();i++){
if(S[i] >= 48 && S[i] <= 57)
a[i] = S[i]-48;
else
a[i] = S[i]-55;
}
len = S.length();
while(!judge() && ans <= 30)
addnumber();
if(ans <= 30)
cout << "STEP=" << ans << endl;
else
cout << "Impossible!" << endl;
return 0;
}