描述
若一个数(首位不为零)从左向右读与从右向左读都一样,我们就将其称之为回文数。
例如:给定一个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,其中16进制数字为0-9与A-F,求最少经过几步可以得到回文数。如果在30步以内(包含30步)不可能得到回文数,则输出“Impossible!”
格式
输入格式
共两行
第一行为进制数N(2<=N<=10或N=16)
第二行为N进制数M(0<=M<=maxlongint)
输出格式
共一行
第一行为“STEP=”加上经过的步数或“Impossible!”
样例1
样例输入1
9
87
样例输出1
STEP=6
限制
各个测试点1s
来源
NOIP1999提高组第2题
问题链接:Vijos P1304 回文数
问题分析:
这个问题是进位有关的问题,给的数进位不定,不能按照10进制进行处理,如何存储数是关键。
阿拉伯记数法中,高位在左边,低位在右边。
程序读入数据时,是从左到右的。
数存储在数组中,低位从下标0开始存放,高位放在下标大的元素单元中,计算处理比较方便。
数组v[]用于存储数,每个元素存放1位,低位从下标0开始存放,高位放在下标大的元素单元中。
数组rv[]用于存储逆序的数。
变量len的值是数的位数。
程序中的各个函数功能,参见源程序。
题记:
写程序就要写简洁的程序。
采用合适的数据表示,可以大幅简化程序逻辑。
参考链接:(略)
AC的C++程序如下:
#include <iostream> #include <string> using namespace std; const int N = 30; const int N2 = 20; char v[N2], rv[N2], len; // 函数功能:判断n是否为回文数 bool isPalindrome(int len, char v[]) { int start = 0, end = len - 1; while(start < end) { if(v[start] != v[end]) return false; start++, end--; } return true; } // 将字符串转换为整数:按位存储到数组,低位从下标0开始存放,高位放在大下标的单元 inline int myatoi(string& s, char v[]) { int len = s.length(); for(int i=len-1,j=0; i>=0; i--,j++) if(s[i] >= 'A') v[j] = s[i] - 'A' + 10; else v[j] = s[i] - '0'; return len; } // 将数组v[]中的数,逆序放到数组rv[]中 inline void setRight(int len, char v[], char rv[]) { for(int i=0; i<len; i++) rv[len - 1 - i] = v[i]; } // 两数相加 inline int add(int len, char v[], char rv[], char base) { int carry = 0; for(int i=0; i<len; i++) { v[i] += rv[i] + carry; carry = v[i] / base; v[i] %= base; } if(carry > 0) v[len++] = carry; return len; } int main() { int n, i; string m; cin >> n >> m; len = myatoi(m, v); for(i=1; i<=N; i++) { setRight(len, v, rv); len = add(len, v, rv, n); if(isPalindrome(len, v)) break; } if(i <= N) cout << "STEP=" << i << endl; else cout << "Impossible!" << endl; return 0; }