洛谷P1015 [NOIP 1999 普及组] 回文数 题解
题目描述
P1015 回文数 是NOIP 1999普及组的经典模拟题。题目要求如下:
给定一个数N(十进制)和进制K(2≤K≤16),将N转换为K进制表示后,通过以下操作使其变为回文数:
- 将当前数与其逆序数相加
- 重复操作直到得到回文数或超过30次操作
输入格式:
- 第一行输入进制K(2≤K≤16)
- 第二行输入十进制数N(1<N≤1e9)
输出格式:
- 若30步内得到回文数,输出
STEP=步数
- 否则输出
Impossible!
解题思路
本题是典型的进制转换+模拟操作问题,核心在于:
- 正确实现大数的K进制转换
- 高效处理大数加法与进位
- 准确判断回文数
关键算法步骤
- 进制转换:将十进制数N转换为K进制表示
- 回文判断:检查当前数是否为回文
- 加法模拟:实现大数与其逆序数的加法运算
- 进位处理:处理不同进制下的进位逻辑
代码解析(附用户代码讲解)
以下是用户提供的代码的逐层解析:
#include<bits/stdc++.h>
#include<string>
using namespace std;
int N; // 目标进制
int step = 0; // 操作步数
int len = 0; // 当前数位长度
int ans[205] = {0}; // 存储K进制数(低位在前)
int tem[205] = {0}; // 临时反转数组
int re[205] = {0}; // 未使用(可忽略)
// 回文数检查函数
int Check() {
for(int i=0; i<len/2; ++i) {
if(ans[i] != ans[len-i-1]) return -1;
}
return 1;
}
// 加法操作函数
void play() {
// 反转数组到tem
for(int i=0; i<len; ++i)
tem[i] = ans[len-i-1];
// 执行加法
for(int i=0; i<len; ++i)
ans[i] += tem[i];
// 进位处理
for(int i=0; i<len; ++i) {
if(N != 16) { // 非16进制通用处理
if(ans[len-1] >= N) len++;
ans[i+1] += ans[i] / N;
ans[i] %= N;
} else { // 16进制特殊处理
if(ans[len-1] >= 16) len++;
ans[i+1] += ans[i] / 16;
ans[i] %= 16;
}
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
string s;
cin >> N >> s;
len = s.length();
// 初始化K进制数(注意低位存储方式)
for(int i=0; i<len; ++i) {
if(isdigit(s[len-i-1]))
ans[i] = s[len-i-1] - '0';
else
ans[i] = s[len-i-1] - 'A' + 10;
}
if(Check() == 1) { // 初始即为回文
cout << "STEP=0";
} else {
while(step <= 30) {
play();
step++;
if(Check() == 1) break;
}
if(step <= 30)
cout << "STEP=" << step;
else
cout << "Impossible!";
}
return 0;
}
代码特点分析
- 低位优先存储:使用数组
ans[]
的低位在前方式存储,简化进位操作 - 双模式进位处理:通过
N != 16
判断统一处理不同进制 - 动态长度维护:通过
len
变量动态跟踪当前数位长度
优化方案与注意事项
优化方向
- 预分配数组空间:可预先分配200位空间,避免频繁扩容
- 提前终止判断:在加法操作前即可判断是否已产生回文
- 进制处理统一化:将16进制特殊处理合并到通用逻辑中
关键细节说明
输入处理:
- 使用
len-i-1
实现字符串反转初始化 - 正确处理字母A-F(10-15)的转换
- 使用
进位逻辑:
- 从低位到高位处理进位
- 每次加法后检查最高位是否需要扩容
回文判断:
- 只需检查前半部分与对应后半部分是否相等
测试样例分析
示例1
输入:
9
87
输出:
STEP=4
过程:
87(10) = 106(9)
106 + 601 = 707(回文,4步)
示例2
输入:
10
196
输出:
Impossible!
(著名的回文数猜想反例)
复杂度分析
- 时间复杂度:O(30*L),其中L为最大数位长度(≤200)
- 空间复杂度:O(L),使用固定大小数组存储
总结
- 本题核心是掌握大数运算在模拟题中的应用
- 关键点在于:
- 正确实现进制转换
- 高效处理大数加法与进位
- 准确判断回文结构
- 实际编程时需特别注意:
- 数组越界问题(动态维护len变量)
- 不同进制的字符处理(0-9和A-F)
- 步骤计数器的初始值与终止条件
通过本题可以巩固:
- 进制转换的双向实现
- 大数运算的基本技巧
- 模拟算法的优化策略