P1217 [USACO1.5] 回文质数 Prime Palindromes

解题思路

  1. 构造优于枚举:直接遍历 \(1\) 亿个数判断是否为回文质数会超时。更高效的方法是构造回文数,然后判断其是否为质数。
  2. 数学剪枝
    • 偶数位回文数性质:除了 \(11\) 以外,所有偶数位(4 位、6 位、8 位...)的回文数都能被 \(11\) 整除(例如 \(1221\)\(5775\)),因此一定不是质数。
    • 最大范围:因为题目上限是 \(10^8\),而 8 位回文数可以直接跳过,所以我们只需要构造 1 位、2 位、3 位、5 位、7 位 的回文数。
    • 首位判断:回文数的首位也是末位,偶数肯定不是质数(除了 2,但题目 \(a \ge 5\)),所以首位数字 d1 只能是 \(1, 3, 5, 7, 9\)

AC 代码

#include <iostream>
#include <cmath>
#include <algorithm>

using namespace std;

int a, b;

// 质数判断函数
bool isPrime(int n) {
    if (n < 2) return false;
    for (int i = 2; i * i <= n; i++)
        if (n % i == 0) return false;
    return true;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);

    cin >> a >> b;

    // 优化:8位回文数必含因子11,故只需搜索到 10,000,000 (7位)
    if (b > 10000000) b = 10000000;

    // 1. 处理特殊的小数 (1位 和 2位)
    // 只有 5, 7, 11 是我们需要关心的
    int small[] = {5, 7, 11};
    for (int x : small) {
        if (x >= a && x <= b && isPrime(x)) cout << x << endl;
    }

    // 2. 构造 3 位回文数 (100~999)
    // 形式: d1 d2 d1
    for (int d1 = 1; d1 <= 9; d1 += 2) { // 首位必须是奇数
        for (int d2 = 0; d2 <= 9; d2++) {
            int p = d1 * 100 + d2 * 10 + d1;
            if (p > b) return 0; // 超出范围直接结束程序
            if (p >= a && isPrime(p)) cout << p << endl;
        }
    }

    // 3. 构造 5 位回文数 (10000~99999)
    // 形式: d1 d2 d3 d2 d1
    for (int d1 = 1; d1 <= 9; d1 += 2) {
        for (int d2 = 0; d2 <= 9; d2++) {
            for (int d3 = 0; d3 <= 9; d3++) {
                int p = d1 * 10000 + d2 * 1000 + d3 * 100 + d2 * 10 + d1;
                if (p > b) return 0;
                if (p >= a && isPrime(p)) cout << p << endl;
            }
        }
    }

    // 4. 构造 7 位回文数 (1000000~9999999)
    // 形式: d1 d2 d3 d4 d3 d2 d1
    for (int d1 = 1; d1 <= 9; d1 += 2) {
        for (int d2 = 0; d2 <= 9; d2++) {
            for (int d3 = 0; d3 <= 9; d3++) {
                for (int d4 = 0; d4 <= 9; d4++) {
                    int p = d1 * 1000000 + d2 * 100000 + d3 * 10000 + d4 * 1000
                          + d3 * 100 + d2 * 10 + d1;
                    if (p > b) return 0;
                    if (p >= a && isPrime(p)) cout << p << endl;
                }
            }
        }
    }

    return 0;
}

关键点总结

  1. 跳过偶数位:代码中直接跳过了 4 位和 6 位的循环,因为它们除了 11 以外不可能是质数。
  2. 首位步长为 2d1 += 2 保证了生成的数是奇数,减少了一半的判断量。
  3. 即时终止:一旦生成的 p > b,直接 return 0 结束程序,因为后续生成的数只会更大。
posted @ 2026-01-27 17:04  张一信奥  阅读(5)  评论(0)    收藏  举报