题解:学而思编程 数字和为x的整数

【题目来源】

学而思编程:数字和为x的整数

【题目描述】

给定一个正整数 \(n\),请从小到大依次输出在 \(1∼n\) 之间(包含 \(1\)\(n\))所有的整数中,各位数字和为 \(x\) 的整数,并统计其出现个数。

【输入】

一行,两个空格隔开正整数 \(n,x\)

【输出】

若干行,每行一个整数,表示各位数字和为 \(x\) 的整数。

最后一行一个整数,表示各位数字和为 \(x\) 的整数出现的个数。

如果不存在各位数字和为 \(x\) 的整数,那么只需要输出 \(−1\)

【输入样例】

1000 26

【输出样例】

899
989
998
3

【核心思想】

  1. 问题分析:给定正整数 \(n\)\(x\),找出 \(1\)\(n\) 中所有各位数字和等于 \(x\) 的整数,从小到大输出并统计个数。这是一个深度优先搜索 + 剪枝问题,关键在于按位数生成数字,并通过数字和与数值上限进行剪枝。

  2. 算法选择

    • DFS 按位生成:从最高位到最低位逐位确定数字,生成所有不超过 \(n\) 位数长度的数字
    • 双重剪枝:当前数值超过 \(n\) 时剪枝;当前数字和超过 \(x\) 时剪枝
    • 标记去重:用 vis 数组标记已找到的数字,避免重复
  3. 关键步骤

    • 初始化:读取 \(n\)\(x\),计算 \(n\) 的位数 len
    • DFS 搜索dfs(step)):
      • 计算当前已生成数字的数值 sum 和各位数字之和 res
      • sum > n:剪枝返回
      • res > x:剪枝返回
      • step == len(已生成完整位数):
        • res == x!vis[sum]:找到符合条件的数字,cnt++,标记 vis[sum] = true
        • 返回
      • 遍历当前位 \(i\)\(0\)\(9\)
        • path[step] = i
        • dfs(step + 1)
    • 输出结果
      • cnt == 0:输出 \(-1\)
      • 否则:输出所有符合条件的数字,最后输出 cnt
  4. 时间/空间复杂度

    • 时间复杂度:\(O(10^{len})\),最坏情况遍历所有 \(len\) 位数,但剪枝大幅优化
    • 空间复杂度:\(O(len)\),递归深度为 len
  5. DFS 剪枝的核心思想

    • 按位生成:从高位到低位逐位确定,天然保证生成数字的递增顺序(配合输出时排序或按序存储)
    • 数值上限剪枝:当前前缀已大于 \(n\) 时,后续任何填充都会超出范围,直接返回
    • 数字和剪枝:当前数字和已超过 \(x\) 时,后续添加非负数字只会更大,不可能等于 \(x\),直接返回
    • 位数控制:只生成 len 位数字,避免短数字的重复生成(如 \(089\)\(89\)
    • 适用于"数位约束下的数字枚举"类问题,DFS 配合剪枝可高效搜索解空间

【算法标签】

DFS-一维

【代码详解】

#include <bits/stdc++.h>
using namespace std;

const int N = 10;  // 定义最大数字长度
int n, x, len, cnt;  // n: 上限数字,x: 目标数字和,len: n的位数,cnt: 符合条件的数字计数
int path[N];        // 记录当前生成的数字各位
int str[N];         // 存储符合条件的数字(实际未使用完整功能)
bool vis[2000005];  // 标记数字是否已被记录(去重)

// 深度优先搜索生成所有可能的数字
void dfs(int step) {
    // 计算当前已生成的部分数字的值
    int sum = 0;
    for (int i = 0; i < step; i++) {
        sum = sum * 10 + path[i];
    }
    // 剪枝:如果当前数字已超过上限n,停止搜索
    if (sum > n) return;

    // 计算当前数字各位之和
    int res = 0;
    for (int i = 0; i < step; i++) {
        res += path[i];
    }
    // 剪枝:如果当前数字各位之和已超过x,停止搜索
    if (res > x) return;

    // 如果生成了完整位数的数字,检查是否满足条件
    if (step == len) {
        if (res == x && !vis[sum]) {  // 数字和等于x且未被记录
            cnt++;
            str[cnt] = sum;  // 存储符合条件的数字
            vis[sum] = true;  // 标记该数字已记录
        }
        return;
    }

    // 递归生成下一位数字(0-9)
    for (int i = 0; i <= 9; i++) {
        path[step] = i;
        dfs(step + 1);
    }
}

// 计算数字n的位数
int calc(int n) {
    int res = 0;
    while (n) {
        res++;
        n /= 10;
    }
    return res;
}

int main() {
    cin >> n >> x;  // 输入上限数字n和目标数字和x
    len = calc(n);  // 计算n的位数

    dfs(0);  // 从第0位开始生成数字

    if (cnt == 0) {
        cout << -1 << endl;  // 没有符合条件的数字
    } else {
        // 输出所有符合条件的数字(注意:这里str数组只存储了最后一个数字)
        for (int i = 1; i <= cnt; i++) {
            cout << str[i] << endl;
        }
        cout << cnt << endl;  // 输出符合条件的数字总数
    }

    return 0;
}

【运行结果】

1000 26
899
989
998
3
posted @ 2026-07-05 14:34  团爸讲算法  阅读(7)  评论(0)    收藏  举报