说明
若一个数(首位不为零)从左向右读与从右向左读都一样,我们就将其称之为回文数。
例如:给定一个10进制数56,将56加56(即把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,每组的第一行输入N,第二行输入M。
输出格式
对于每组输入数据,输出最少经过几步可以得到回文数。如果在30步以内(包含30步)不可能得到回文数,则输出"Impossible!"。
样例
9
87
STEP=6
c语言版:
#include <stdio.h>
#include <string.h>
#define MAX 5000 // 定义字符数组的最大长度
int len, d, lim = 30; // len表示字符串长度,d表示进制,lim表示最多进行30次操作
char str[MAX], rts[MAX]; // str用于存储原始字符串,rts用于存储反转后的字符串
// 将字符串b反转,并存入字符串a中
void fz(char a[MAX], char b[MAX]) {
// 从b的末尾依次赋值到a的前面,实现反转
for (int i = 0; i < len; ++i) {
a[i] = b[len - 1 - i];
}
a[len] = '\0'; // 确保字符串末尾是'\0'结束符
}
// 实现字符串str和rts的按进制d的加法,并存入rts
void add() {
int carry = 0; // carry用于存储进位
for (int i = 0; i < len; ++i) {
// 将str和rts对应位相加,并加上进位carry
rts[i] += str[i] - '0' + carry;
carry = (rts[i] - '0') / d; // 计算进位值
rts[i] = (rts[i] - '0') % d + '0'; // 当前位取模,重新变成字符
}
// 如果有进位,处理进位部分
if (carry) {
rts[len] = '0' + carry;
rts[++len] = '\0'; // 增加新的一位,并确保字符串以'\0'结尾
}
}
// 判断当前字符串rts是否为回文
int judge() {
// 从两端向中间逐位比较字符
for (int i = 0; i < len; ++i) {
if (rts[i] != rts[len - 1 - i]) {
return 0; // 如果存在不相等的字符,返回0表示不是回文
}
}
return 1; // 如果所有字符都相等,返回1表示是回文
}
int main() {
// 输入进制d和字符串str
scanf("%d %s", &d, str);
// 将输入字符串中的大写字母转换为相应的数字
for (int i = 0; i < strlen(str); ++i) {
if ('A' <= str[i] && str[i] <= 'Z') {
str[i] = str[i] - 'A' + '9' + 1; // 将'A'-'Z' 转换为 10-35
}
}
// 最多进行lim(30次)的操作,寻找回文数
for (int i = 0; i <= lim; ++i) {
len = strlen(str); // 获取当前字符串长度
fz(rts, str); // 将str反转存入rts中
// 判断rts是否为回文,如果是则退出循环
if (judge()) {
printf("STEP=%d\n", i); // 输出找到回文的步骤数
return 0;
}
// 如果不是回文,则将str和rts相加,并存入rts中
add();
fz(str, rts); // 将rts反转后重新存入str,进行下一次循环
}
// 如果超过30次仍然没有找到回文,则输出"Impossible!"
printf("Impossible!\n");
return 0;
}
解释:
-
主函数:
- 输入进制
d
和字符串str
。 - 将输入字符串中的大写字母转换为对应的数字(A 转换为 10,B 转换为 11,依次类推)。
- 循环最多进行
lim
次操作,每次检查是否为回文,如果是回文则输出操作步数STEP=n
,如果超过lim
次仍未找到回文则输出Impossible!
。
- 输入进制
C 语言版本的关键点:
- C 语言中数组操作需要手动管理字符数组的末尾
\0
,这是与 C++ 中string
的区别之一。 - 输入与输出使用
scanf
和printf
,比 C++ 更为低级,但更加高效
C++
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef vector<ll> vll;
typedef pair<int,int> pii;
#define endl '\n'
const ll MAXN = 2e5+10; // 定义最大数组长度
char letter[20] = "0123456789ABCDEF"; // 进制表示用到的字符表
// 将字符转换为数字的函数
int char_to_int(char c) {
// 如果是数字字符(0-9),则返回数字本身;如果是字母(A-F),则返回对应的数值
return isdigit(c) ? c - '0' : c - 'A' + 10;
}
// 将数字转换为字符的函数
char int_to_char(int n) {
// 返回数字对应的进制字符
return letter[n];
}
// 反转字符串并进行两数相加的函数,返回相加后的字符串
string radd(string a, int k) {
string b = a;
reverse(b.begin(), b.end()); // 将字符串 a 反转,存储到 b 中
string ans; // 用于存储最终的结果字符串
vector<int> nums(a.length() + 1, 0); // 动态分配数组用于存储每一位的加和结果,并初始化为 0
int aL = a.length(); // 获取字符串 a 的长度
// 将字符转换为数字并进行对应位置的加法
for (int i = 0; i < aL; i++) {
int numa = char_to_int(a[i]); // 将 a[i] 转换为相应的数值
int numb = char_to_int(b[i]); // 将 b[i] 转换为相应的数值
nums[i] += numa + numb; // 对应位的两个数字相加
}
// 处理进位
for (int i = 0; i < aL; i++) {
if (nums[i] >= k) { // 如果当前位大于等于进制 k,则需要进位
nums[i+1] += nums[i] / k; // 进位给下一位
nums[i] %= k; // 当前位保留进制范围内的值
}
}
if (nums[aL]) aL++; // 如果最后有进位,则需要增加长度
// 将结果转换回字符串
for (int i = aL - 1; i >= 0; i--) {
ans += int_to_char(nums[i]); // 将数字转换为字符并拼接到结果字符串中
}
return ans; // 返回相加后的字符串
}
int main() {
ios_base::sync_with_stdio(false); // 提高输入输出效率,关闭 C 风格的同步
cin.tie(nullptr); // 取消 cin 和 cout 的绑定,进一步提升性能
string M; // 用于存储输入的数字字符串
int N, cnt = 1; // N 表示输入的进制,cnt 表示步骤计数,初始化为 1
cin >> N >> M; // 输入进制 N 和数字字符串 M
string temp = radd(M, N), s; // 调用 radd 函数将 M 和它的反转相加,存储到 temp 中
s = temp;
reverse(s.begin(), s.end()); // 将 temp 反转,存储到 s 中
// 循环直到找到回文数或者超过 30 步
while (temp != s) { // 当 temp 不是回文时
cnt++; // 步骤计数器加 1
if (cnt > 30) { // 如果步骤超过 30,则输出 "Impossible!"
cout << "Impossible!" << endl;
return 0; // 程序结束
}
temp = radd(temp, N); // 将当前字符串 temp 与它的反转相加
s = temp;
reverse(s.begin(), s.end()); // 将新的 temp 再次反转
}
// 如果找到回文数,输出步骤数
cout << "STEP=" << cnt << endl;
return 0; // 程序结束
}
#include<iostream>
#include<string.h>
using namespace std;
const int Max = 5000; // 最大字符数组大小
int len, d, lim = 30; // len表示字符串长度,d表示进制,lim表示最多进行30次操作
char str[Max], rts[Max]; // str是原始字符串,rts是反转后的字符串
// 将字符串b反转并存入字符串a中
void fz(char a[Max], char b[Max]) {
// 遍历字符串b,将b的字符从末尾依次复制到a的前面,达到反转的效果
for (int i = 0; i < len; ++i)
a[i] = b[len - 1 - i];
}
// 实现str和rts的加法,并存回rts中,按指定进制d进行进位
void add() {
int c = 0; // 用于保存进位
// 从低位开始将str和rts对应位置的数字相加
for (int i = 0; i < len; ++i) {
rts[i] += str[i] - '0' + c; // 当前位的相加结果存入rts[i]
c = (rts[i] - '0') / d; // 计算进位,当前位的值除以进制d
rts[i] = (rts[i] - '0') % d + '0'; // 当前位的值取模进制d,并转回字符
}
// 如果最高位仍然有进位,则将进位存入rts的下一位
if (c) rts[len] = '0' + c;
}
// 判断当前字符串rts是否为回文
bool judge() {
// 从两端开始向中间逐位比较,如果有不相等的字符则返回false
for (int i = 0; i < len; ++i)
if (rts[i] != rts[len - 1 - i]) return false;
// 如果所有字符都相等,则返回true,表示是回文
return true;
}
int main() {
// 输入进制d和字符串str
cin >> d >> str;
// 将输入字符串中的大写字母转换为对应的数字
// 比如'A' 转换为 10,'B' 转换为 11,依次类推
int i;
for (i = 0; i < strlen(str); ++i) {
if ('A' <= str[i] && str[i] <= 'Z')
str[i] = str[i] - 'A' + '9' + 1; // 大写字母转换为数字字符
}
// 最多进行lim(即30)次的加法反转判断操作
for (i = 0; i <= lim; ++i) {
len = strlen(str); // 计算当前字符串长度
fz(rts, str); // 将str反转并存入rts
// 判断rts是否为回文字符串,如果是则退出循环
if (judge() == true) break;
add(); // 如果不是回文,则将str和rts相加,并将结果存入rts中
len = strlen(rts); // 更新rts的长度
// 将rts再反转存入str,继续下一轮循环
fz(str, rts);
}
// 输出结果,如果在30步以内找到回文,则输出步骤数
if (i <= lim) cout << "STEP=" << i << endl;
// 如果超过30步仍未找到回文,则输出"Impossible!"
else cout << "Impossible!" << endl;
}