题解:洛谷 P1218 特殊的质数肋骨

【题目来源】

洛谷:P1218 [USACO1.5] 特殊的质数肋骨 Superprime Rib - 洛谷

【题目描述】

农民约翰的母牛总是产生最好的肋骨。你能通过农民约翰和美国农业部标记在每根肋骨上的数字认出它们。

农民约翰确定他卖给买方的是真正的质数肋骨,是因为从右边开始切下肋骨,每次还剩下的肋骨上的数字都组成一个质数。

举例来说:7 3 3 1 全部肋骨上的数字 7331 是质数;三根肋骨 733 是质数;二根肋骨 73 是质数;当然,最后一根肋骨 7 也是质数。7331 被叫做长度 4 的特殊质数。

写一个程序对给定的肋骨的数目 n,求出所有的特殊质数。1 不是质数。

【输入】

一行一个正整数 n

【输出】

按顺序输出长度为 n 的特殊质数,每行一个。

【输入样例】

4

【输出样例】

2333
2339
2393
2399
2939
3119
3137
3733
3739
3793
3797
5939
7193
7331
7333
7393

【核心思想】

  1. 问题分析:给定长度 \(n\),要求找出所有长度为 \(n\) 的"特殊质数"——从最高位到最低位逐位截断后得到的每个前缀都是质数。例如 \(7331\) 是特殊质数,因为 \(7\)\(73\)\(733\)\(7331\) 均为质数。这是一个DFS 逐位构造 + 质数剪枝问题。

  2. 算法选择

    • 逐位 DFS 构造:从最高位开始,每次在已有前缀后添加一位数字(\(1 \sim 9\)),形成新数后判断是否为质数
    • 质数剪枝:若当前前缀不是质数,则直接剪枝,不再向下扩展(因为任何以该前缀开头的数都不可能是特殊质数)
    • 试除法判质:对每个构造出的数用 \(\sqrt{n}\) 范围内的试除法判断质数
  3. 关键步骤

    • 读入\(n\)(目标长度)
    • DFS 函数 dfs(cnt, num)
      • cnt:当前已构造的位数
      • num:当前前缀数值
      • 遍历 \(i\)\(1\)\(9\)
        • tmp = num * 10 + i:构造新数
        • isPrime(tmp) 为真:
          • cnt == n:已到达目标长度,输出 tmp
          • 否则:dfs(cnt + 1, tmp) 继续向下构造
    • 启动搜索dfs(1, 0),从 1 位数字、前缀 0 开始
  4. 时间/空间复杂度

    • 时间复杂度:\(O(9^n \cdot \sqrt{10^n})\),实际因大量剪枝远小于此值
    • 空间复杂度:\(O(n)\),递归深度为 \(n\)
  5. DFS 逐位构造的核心思想

    • 前缀保持性:特殊质数的任意前缀也必须是特殊质数,因此可以从高位到低位逐位构造,每步验证
    • 强剪枝效果:一位数质数只有 \(2, 3, 5, 7\) 四个,以此为前缀的二位数质数更少,搜索树分支极少
    • 逐层筛选:每深入一层就过滤掉大量非质数分支,\(n\) 较大时依然高效
    • 无后效性:当前前缀是否为质数只取决于前缀本身,与后续添加的数字无关
    • 适用于逐位构造、前缀约束、强剪枝搜索类问题

【解题思路】

【算法标签】

普及- #DFS-一维

【代码详解】

#include <bits/stdc++.h>
using namespace std;
int n, st=1, ed=9, t, tmp;
bool isPrime(int n)  // 定义质数模板
{
    if (n<2) return false;
    for (int i=2; i<=sqrt(n); i++) {
        if (n%i==0) return false;
    }
    return true;
}
void dfs(int cnt, int num)  // 定义深搜函数
{
    for (int i=1; i<=9; i++) {  // 遍历1-9
        int tmp = num * 10 + i;  // 使用临时变量tmp记录进位后的数字
        if (isPrime(tmp)) {  // 判断这个数字是否是质数
            if (cnt==n) {  // 判断此时的cnt是否为n
                cout << tmp << endl;  // 如果是则输出tmp
            } else {  // 如果cnt还不是n,但因为当前数字已经是质数,所以要进一步搜索
                dfs(cnt+1, tmp);  // cnt自增1,使用tmp继续搜索
            }
        }
    }

}
int main()
{
    cin >> n;  // 输入n
    dfs(1, 0);  // 使用深搜,1表示1位数字,0表示起始数字为0
    return 0;
}

【运行结果】

4
2333
2339
2393
2399
2939
3119
3137
3733
3739
3793
3797
5939
7193
7331
7333
7393
posted @ 2026-08-03 08:46  团爸讲算法  阅读(17)  评论(0)    收藏  举报