题解:学而思编程 素数加法算式

【题目来源】

学而思编程:素数加法算式

【题目描述】

有一个只含数字 \(1\sim 9\) 的字符串 \(s\),现在要求在字符中插入若干加号,使得字符串成为一个加法算式。(也可以一个都不加)

得到的加法算式的计算结果是素数的算式有多少种?

【输入】

\(1\) 行,\(1\) 个字符串 \(s\)

【输出】

输出结果是素数的算式个数

【输入样例】

124

【输出样例】

1

【核心思想】

  1. 问题分析:给定只含数字 \(1 \sim 9\) 的字符串 \(s\),需要在字符之间插入若干加号(也可不插),使其成为一个加法算式,统计计算结果为素数的算式个数。例如 124 可分割为 1+2+4=712+4=161+24=25124=124,其中只有 7 是素数,故答案为 \(1\)。关键观察是:\(n\) 个字符之间有 \(n-1\) 个可分割位置,每个位置有"分割"或"不分割"两种选择,共 \(2^{n-1}\) 种方案,适合用 DFS 枚举。

  2. 算法选择

    • 深度优先搜索(DFS):枚举每个可分割位置的两种状态(分割/不分割),生成所有可能的加法算式
    • 试除法判定素数:对每个算式的结果,用 \(O(\sqrt{x})\) 的试除法判断是否为素数
  3. 关键步骤

    • 初始化:读入字符串 \(s\),长度 \(n = |s|\),设置 \(c[n] = 1\)(最后一个位置强制分割,确保最后一段数字被计入总和)
    • DFS 枚举dfs(p)\(p\) 为当前处理的分割位置):
      • \(p > n - 1\):所有位置已处理完毕,计算当前方案的总和:
        • 遍历字符串,根据 \(c[i]\) 的值决定分割或拼接数字
        • isp(sum) 为真,cnt++
        • 返回
      • \(c[p] = 0\),递归 dfs(p + 1)(不分割)
      • \(c[p] = 1\),递归 dfs(p + 1)(分割)
    • 素数判定 isp(x)
      • \(x < 2\),返回 false
      • 遍历 \(i\)\(2\)\(\lfloor \sqrt{x} \rfloor\):若 \(x \bmod i = 0\),返回 false
      • 返回 true
    • 输出答案\(cnt\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(2^{n-1} \cdot n \cdot \sqrt{S})\),其中 \(S\) 为算式结果的最大值。\(2^{n-1}\) 种方案,每种方案计算总和 \(O(n)\),素数判定 \(O(\sqrt{S})\)
    • 空间复杂度:\(O(n)\),递归栈深度和分割状态数组
  5. DFS 枚举与剪枝的核心思想

    • 二进制状态枚举\(n-1\) 个分割位置对应 \(n-1\) 个二进制位,DFS 本质是枚举所有 \(2^{n-1}\) 种二进制状态,每种状态对应一种唯一的分割方案
    • 强制边界分割:设置 \(c[n] = 1\) 确保字符串末尾必然形成一个完整的加数,避免最后一段数字被遗漏,简化了总和计算的逻辑
    • 延迟计算策略:不在 DFS 过程中实时维护当前总和,而是在叶子节点(所有位置确定后)统一计算,降低了状态维护的复杂度
    • 试除法的适用性:由于字符串只含 \(1 \sim 9\) 且长度通常较小(\(n \leq 10\) 左右),算式结果不会太大,试除法足够高效
    • 适用于组合枚举、字符串分割、表达式生成等场景,是 DFS 在组合计数问题中的典型应用

【解题思路】

【算法标签】

DFS-一维

【代码详解】

#include <bits/stdc++.h>
using namespace std;
const int MR = 1e6 + 10;
typedef long long LL;
string s;
int n;
bool isp(long long x)
{
    if (x < 2) return false;  // 小于2的不是素数
    for (int i = 2; i * i <= x; i++) {  // 试除法判断素数
        if (x % i == 0) return false;
    }
    return true;
}
int c[11], cnt;  // c数组记录是否分割,cnt记录方案数

void dfs(int p)  // 递归枚举所有分割方案
{
    if (p > n - 1) {  // 已经处理完所有分割位置
        long long sum = 0, t = (s[0] - '0');  // sum为总和,t为当前数字
        for (int i = 1; i <= n; i++) {
            if (c[i] == 0) {  // 不分割,继续组成数字
                t = t * 10 + (s[i] - '0');
            } else {  // 分割,将当前数字加入总和
                sum += t;
                t = (s[i] - '0');  // 开始新的数字
            }
        }
        if (isp(sum)) cnt++;  // 判断总和是否为素数
        return;
    }
    c[p] = 0;  // 在第p个位置不分割
    dfs(p + 1);
    c[p] = 1;  // 在第p个位置分割
    dfs(p + 1);
}

int main()
{
    cin >> s;  // 输入数字字符串
    n = s.size();  // 获取长度
    c[n] = 1;  // 最后一个位置强制分割,确保最后一段数字被加入总和
    dfs(1);  // 从第一个分割位置开始枚举
    cout << cnt << endl;
    return 0;
}

【运行结果】

124
1
posted @ 2026-07-02 22:45  团爸讲算法  阅读(4)  评论(0)    收藏  举报