题解:洛谷 P1207 双重回文数

【题目来源】

洛谷:P1207 [USACO1.2] 双重回文数 Dual Palindromes - 洛谷

【题目描述】

如果一个数从左往右读和从右往左读都是一样,那么这个数就叫做“回文数”。例如,12321 就是一个回文数,而 77778 就不是。当然,回文数的首和尾都应是非零的,因此 0220 就不是回文数。

事实上,有一些数(如 21 ),在十进制时不是回文数,但在其它进制(如二进制时为 10101 )时就是回文数。

给定两个十进制数 n,s,然后找出前 n 个满足大于 s 且在不少于两种进制(二进制至十进制)上是回文数的十进制数,输出到文件上。

本问题的解决方案不需要使用大于 32 位的整型数

【输入】

只有一行,用空格隔开的两个正整数 n,s

【输出】

n 行,每行一个满足上述要求的数,并按从小到大的顺序输出。

【输入样例】

3 25

【输出样例】

26
27
28

【核心思想】

  1. 问题分析:给定两个正整数 \(n\)\(s\),要求找出前 \(n\)大于 \(s\) 的十进制数,使得每个数在至少两种不同进制(二进制到十进制)下都是回文数,并按升序输出。这是 P1206「回文平方数」的扩展版,从单一进制的回文判断推广到多进制回文计数

  2. 算法选择

    • 顺序枚举:从 \(s+1\) 开始逐个检查整数,天然保证输出结果有序
    • 进制转换:复用短除法模板,将十进制数 \(i\) 转换为 \(k\) 进制字符串
    • 回文判断:复用字符串对称比较,判断转换结果是否为回文
    • 计数统计:对每个整数,统计其在 \(2 \sim 10\) 进制中有多少种进制表示是回文
    • 提前终止:一旦某数已有 2 种回文进制即输出;收集到 \(n\) 个结果后整体结束
  3. 关键步骤

    • 读取输入\(n\)(需要输出的个数)、\(s\)(下界)
    • 枚举候选数\(i\)\(s+1\) 开始递增
    • 重置计数器\(mark1 = 0\)(当前数回文进制数)
    • 遍历进制\(k\)\(2\)\(10\)):
      • \(i\) 转换为 \(k\) 进制字符串
      • 判断是否为回文
      • 若是,\(mark1++\)
      • \(mark1 == 2\),输出 \(i\),总输出计数 \(mark2++\),并跳出当前进制循环
    • 终止条件:若 \(mark2 == n\),结束整个枚举
  4. 时间/空间复杂度

    • 时间复杂度:\(O(n \cdot 9 \cdot \log(\text{max\_value}))\),其中 \(\text{max\_value}\) 为找到 \(n\) 个答案时扫描到的最大数值;由于满足双重回文的数出现较频繁,实际运行效率足以通过
    • 空间复杂度:\(O(\log(\text{max\_value}))\),存储每次进制转换产生的字符串
  5. 多进制回文计数的核心思想

    • 模块化复用:进制转换与回文判断函数可直接从 P1206 复用,体现算法题的模板化思维
    • 从小到大枚举:从 \(s+1\) 开始递增遍历,天然保证输出结果按升序排列
    • 计数而非穷举所有进制:不需要检查完 9 种进制,只要累计到 2 种即可提前确认当前数有效
    • 双重终止策略:内层找到 2 种回文即 break,外层找到 \(n\) 个数即 break,避免无效计算
    • 适用于多进制判定、条件计数、按序输出满足某性质的数等问题

【解题思路】

【算法标签】

普及- #模拟

【代码详解】

#include <bits/stdc++.h>
using namespace std;
int n, s, mark1, mark2=0;
string dtok(int t, int k)  // 十进制转k进制模板
{
    string ans = "";  // 定义空字符串
    while (t>0) {  // 使用数位拆分思路,当t不为0时
        int x = t % k;  // 除以k,得到k进制的余数
        if (x<10) ans = char(x+'0') + ans;  // 拼接到ans字符串的前面(注意不是追加到后面)
        else ans = char(x-10+'A') + ans;  // 对于大于10的,转为大写字母
        t = t / k;  // 更新t为整数部分
    }
    if (ans.empty()) ans = "0";  // 特判t为0时,直接返回0
    return ans;  // 返回拼接后的字符串
}

bool isPal(string s) {  // 回文字符串的判断
    for (int i=0; i<s.size(); i++) {  // 从头开始遍历
        if (s[i]!=s[s.size()-1-i]) {  // 逐位比较,如果遇到不相等的,就返回false
            return false;
        }
    }
    return true;  // 遍历完还没false的就返回true
}
int main()
{
    cin >> n >> s;  // 输入n和s
    for (int i=s+1; ; i++) {  // 从s+1开始遍历(注意不是s!!!)
        mark1=0;  // 每次开始mark1重置为0
        for (int k=2; k<=10; k++) {  // 从二进制遍历至十进制
            if (isPal(dtok(i, k))) {  // 如果进制转换后是回文串
                mark1++;  // mark1自增
            }
            if (mark1==2) {  // 当有两种满足
                cout << i << endl;  // 则输出
                mark2++;  // 并且mark2自增1
                break;  // 退出循环,使用下一个i
            }
        }
        if (mark2==n) break;  // 当输出了n行,则停止循环
    }
    return 0;
}

【运行结果】

3 25
26
27
28
posted @ 2026-07-30 10:15  团爸讲算法  阅读(3)  评论(0)    收藏  举报