「枚举」组合的输出
本题为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;
}
"正是我们每天反复做的事情,最终造就了我们,优秀不是一种行为,而是一种习惯" ---亚里士多德
这里是浙江理工大学22届ACM集训队的成员一枚鸭!
本文首发于博客园,作者:星双子,除了我自己的转载请注明原文链接:https://www.cnblogs.com/geministar/p/zstu23_3_22_A.html