题解:洛谷 P1157 组合的输出

【题目来源】

洛谷:P1157 组合的输出 - 洛谷(luogu.com.cn)

【题目描述】

排列与组合是常用的数学方法,其中组合就是从 \(n\) 个元素中抽出 \(r\) 个元素(不分顺序且 \(r\le n\)),我们可以简单地将 \(n\) 个元素理解为自然数 \(1,2,\dots,n\),从中任取 \(r\) 个数。

现要求你输出所有组合。

例如 \(n=5,r=3\),所有组合为:

\(123,124,125,134,135,145,234,235,245,345\)

【输入】

一行两个自然数 \(n,r(1\lt n\lt 21,0\le r\le n)\)

【输出】

所有的组合,每一个组合占一行且其中的元素按由小到大的顺序排列,每个元素占三个字符的位置,所有的组合也按字典顺序。

注意哦!输出时,每个数字需要 \(3\) 个场宽。以 C++ 为例,你可以使用下列代码:

cout << setw(3) << x;

输出占 \(3\) 个场宽的数 \(x\)。注意你需要头文件 iomanip

【输入样例】

5 3

【输出样例】

  1  2  3
  1  2  4
  1  2  5
  1  3  4
  1  3  5
  1  4  5
  2  3  4
  2  3  5
  2  4  5
  3  4  5

【核心思想】

  1. 问题分析:从 \(1\)\(n\) 的自然数中选出 \(r\) 个数的所有组合,按字典序输出,每个数字占 3 个字符宽度。这是经典的组合枚举问题,可以用深度优先搜索(DFS)生成所有组合,利用参数控制每次选择的起点,保证组合内元素升序且不重复。

  2. 算法选择

    • DFS 枚举组合:递归函数 dfs(start, count),其中 start 表示当前可选的起始数字,count 表示还需选择的数字个数。当 count == 0 时,输出当前组合。循环 istartn,选择 i 并递归 dfs(i+1, count-1)
    • 回溯:在递归返回后取消选择(对于组合问题,覆盖即可,无需显式回溯)。
    • 输出格式:使用 setw(3) 设置每个数字的宽度为 3。
  3. 关键步骤

    • 读入:读取 \(n\)\(r\)
    • DFSdfs(step, num)step 表示当前已选数字个数(从 1 开始),num 表示当前可选的最小数字。
      • step > r,输出 a[1..r](每个占 3 位),返回。
      • 循环 inumna[step] = i,递归 dfs(step+1, i+1)
    • 输出:使用 cout << setw(3) << a[i]
  4. 时间/空间复杂度

    • 时间复杂度:\(O(C(n, r) \times r)\),其中 \(C(n,r)\) 是组合数。
    • 空间复杂度:\(O(r)\),递归深度为 \(r\)
  5. DFS 与组合生成

    • 保证升序:通过 num 参数确保每次选择的数字都比前一个大,从而避免重复组合。
    • 字典序输出:由于循环从小到大,递归生成的组合自然按字典序排列。
    • 适用场景:这类“输出所有组合”的问题,是 DFS 入门经典题。

【解题思路】

【算法标签】

普及- #DFS-一维

【代码详解】

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

int n, k, b[25] = {0};  // 定义全局变量:n-总数,k-选择数,b-标记数组

// 输出当前组合
void shuchu() {
    for (int i = 1; i <= n; i++) {
        if (b[i]) cout << setw(3) << i;  // 输出被选中的数字,宽度为3
    }
    cout << endl;  // 换行
}

// 递归生成组合
void f(int flag, int num) {
    // flag: 当前起始位置
    // num: 剩余需要选择的数字个数

    // 终止条件:已选够k个数字
    if (num == 0) {
        shuchu();  // 输出当前组合
        return;
    }

    // 剪枝:剩余数字不足以选够k个
    if (n - flag + 1 < num) return;

    // 递归枚举所有可能的组合
    for (int i = flag; i <= n; i++) {
        b[i] = 1;       // 选择当前数字
        f(i + 1, num - 1);  // 递归处理后续数字
        b[i] = 0;       // 回溯,取消选择
    }
}

int main() {
    cin >> n >> k;  // 输入总数n和选择数k
    f(1, k);        // 从第1个位置开始,选择k个数字
    return 0;
}
#include <bits/stdc++.h>
using namespace std;

int n, r, a[10];  // 定义全局变量:n-总数,r-选择数,a-存储当前组合

// 深度优先搜索生成组合
void dfs(int step, int num) {
    // step: 当前选择的第几个数字(从1到r)
    // num: 当前可选的起始数字(保证组合有序避免重复)

    // 终止条件:已选够r个数字
    if (step > r) {
        // 输出当前组合(每个数字占5位宽度)
        for (int i = 1; i <= r; i++) {
            cout << setw(5) << a[i];
        }
        cout << endl;
        return;
    }

    // 枚举所有可能的数字(从num到n)
    for (int i = num; i <= n; i++) {
        a[step] = i;          // 选择当前数字
        dfs(step + 1, i + 1); // 递归选择下一个数字(保证升序)
        a[step] = 0;          // 回溯(可省略,因为会被覆盖)
    }
}

int main() {
    cin >> n >> r;  // 输入总数n和选择数r
    dfs(1, 1);      // 从第1个位置开始,起始数字为1
    return 0;
}

【运行结果】

5 3
  1  2  3
  1  2  4
  1  2  5
  1  3  4
  1  3  5
  1  4  5
  2  3  4
  2  3  5
  2  4  5
  3  4  5
posted @ 2026-08-31 09:24  团爸讲算法  阅读(11)  评论(0)    收藏  举报