「枚举」组合的输出

本题为3月23日23上半学期集训每日一题中B题的题解

题面

(写题解的时候学校oj已不可查看此题,下面的题面来自洛谷第1157题)

题目描述

排列与组合是常用的数学方法,其中组合就是从 \(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<n<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

思路分析

非常经典的暴力枚举问题,直接对每一位枚举就行了.学校oj的题目上明确要求用递归的方法进行枚举,所以这里给出的参考代码是递归形式的.其实还可以使用迭代形式的二进制枚举,以及使用C++内置的next_permutation函数.

参考代码

时间复杂度: \(O(\tbinom{N}{R})\)

空间复杂度: \(O(R)\) (计入递归消耗空间)

#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3, "Ofast", "inline")
#include <bits/stdc++.h>

using namespace std;

using i64 = long long;

vector<int> res; // 存放答案

// 爆搜所有组合
void dfs(int n, int i, int r) {
    // 基线条件,当前答案已完成填写,输出
    if (r == 0) {
        for (const auto &i : res) {
            cout << setw(3) << i;
        }
        cout << "\n";
        return;
    }

    // 枚举当前位置的所有可能
    for (; i <= n; i++) {
        res.push_back(i);
        dfs(n, i + 1, r - 1); // 继续枚举下一位置
        res.pop_back(); // 因为是递归,运行到这行时后续位的枚举已经完成,为了当前位枚举下一个可能,需要消除掉本次枚举,所以把最后一个数踢掉(其他数在相应的递归中会被踢掉)
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);W

    int n, r;
    cin >> n >> r;

    res.reserve(r);
    dfs(n, 1, r);

    return 0;
}

"正是我们每天反复做的事情,最终造就了我们,优秀不是一种行为,而是一种习惯" ---亚里士多德

posted @ 2023-03-24 14:14  星双子  阅读(48)  评论(0编辑  收藏  举报