这是算法进阶上的三个例子。
一、指数型枚举
从 1~n 这 n 个整数中随机选取任意多个,输出所有可能的选择方案。
输入格式
输入一个整数n。
输出格式
每行输出一种方案。
同一行内的数必须升序排列,相邻两个数用恰好1个空格隔开。
对于没有选任何数的方案,输出空行。
本题有自定义校验器(SPJ),各行(不同方案)之间的顺序任意。
数据范围
1≤n≤15
每个点有选或不选两种状态,故共有2^n种排列。直接在每次缩小问题时枚举选或不选当前数字两种状态。
- #include <iostream>
- #include <cstdio>
- #include <cstring>
- #include <vector>
- using namespace std;
- int n;
- vector<int> sel;
- void cal(int x) {
- if (x == n + 1) {
- for (int i = 0; i < sel.size(); ++i)
- printf("%d ", sel[i]);
- putchar('\n');
- return;
- }
- cal(x + 1);
- sel.push_back(x);
- cal(x + 1);
- sel.pop_back();
- return;
- }
- int main() {
- cin >> n;
- cal(1);
- }
当然,我们有更神奇的递推做法来枚举二进制子集。(我对它的正确性理解不能
- #include <iostream>
- #include <cstdio>
- using namespace std;
- int main() {
- int n;
- cin >> n;
- int s = (1 << n) - 1;
- for (int i = s; i; i = (i - 1) & s) {
- for (int j = 0; j < n; ++j) if (i >> j & 1)
- printf("%d ", j + 1);
- putchar('\n');
- }
- putchar('\n');
- return 0;
- }
二、递归实现组合型枚举
从 1~n 这 n 个整数中随机选出 m 个,输出所有可能的选择方案。
输入格式
两个整数 n,m ,在同一行用空格隔开。
输出格式
按照从小到大的顺序输出所有方案,每行1个。
首先,同一行内的数升序排列,相邻两个数用一个空格隔开。
其次,对于两个不同的行,对应下标的数一一比较,字典序较小的排在前面(例如1 3 5 7排在1 3 6 8前面)。
数据范围
n>0 ,
0≤m≤n ,
n+(n−m)≤25
要保证选m个,给指数型枚举加上一个剪枝就好了。如果要按字典序枚举,只要从小到大枚举数字,优先选择选当前的数递归即可。
- #include <cstdio>
- #include <vector>
- #include <iostream>
- using namespace std;
- int n, m;
- vector<int> sel;
- void cal(int x) {
- if (sel.size() > m || sel.size() + (n - x + 1) < m)
- return;
- if (x == n + 1) {
- for (int i = 0; i < sel.size(); ++i)
- printf("%d ", sel[i]);
- putchar('\n');
- return;
- }
- sel.push_back(x);
- cal(x + 1);
- sel.pop_back();
- cal(x + 1);
- return;
- }
- int main() {
- cin >> n >> m;
- cal(1);
- }
三、递归实现排列型枚举
枚举全排列用库函数next_permutation()即可。用递归来实现的话,我们从当前未选择的数中从小到大枚举下一个位置上的数,同样可以保证按字典序枚举。
- #include <iostream>
- #include <cstdio>
- #include <cstring>
- #include <algorithm>
- using namespace std;
- int a[11], n;
- bool vis[11];
- void cal(int k) {
- if (k == n + 1) {
- for (int i = 1; i <= n; ++i)
- printf("%d ", a[i]);
- putchar('\n');
- return;
- }
- for (int i = 1; i <= n; ++i) {
- if (vis[i]) continue;
- a[k] = i;
- vis[i] = true;
- cal(k + 1);
- vis[i] = false;
- }
- return;
- }
- int main() {
- cin >> n;
- cal(1);
- }
- int mainx() { //库函数实现
- cin >> n;
- for (int i = 1; i <= n; ++i)
- a[i] = i;
- do {
- for (int i = 1; i <= n; ++i)
- printf("%d ", a[i]);
- putchar('\n');
- } while (next_permutation(a + 1, a + 1 + n));
- return 0;
- }