原题链接:递归实现指数型枚举
题目描述:
从 1~n 这 n 个整数中随机选取任意多个,输出所有可能的选择方案。
输入格式
输入一个整数n。
输出格式
每行输出一种方案。
同一行内的数必须升序排列,相邻两个数用恰好1个空格隔开。
对于没有选任何数的方案,输出空行。
本题有自定义校验器(SPJ),各行(不同方案)之间的顺序任意。
数据范围
1≤n≤15
输入样例:
3
输出样例:
3
2
2 3
1
1 3
1 2
1 2 3
方法一:递归+位运算
思路:二进制都是0与1,用位运算正好可以标记每一个数是否被选择,并且不会占用太多空间。
时间复杂度估算:O(n^2)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
int n;
//递归+位运算
void dfs(int u, int state)
{
if(u == n)
{
for(int i = 0; i < n; i++)
if(state >> i & 1) cout << i + 1 << " ";//如果第i位是被选到(标记)过的,就输出i+1
cout << endl;//因为题目是有说过如果没选也要输出空行,所以不用担心PE
return ;
}
dfs(u + 1, state);//第u位没有被选
dfs(u + 1, state | (1 << u));//第u位被选了
}
int main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin >> n;
dfs(0, 0);//从0开始,为了进行位运算。
return 0;
}
方法二:纯递归
思路:将同一个数分成“选”与“不选”两条分支。每一次沿着分支走,未确定的数就减少1,规模更小。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
int n;
vector<int> v;
void dfs(int u)
{
if(u == n + 1)
{
for(int i = 0; i < v.size(); i++)
cout << v[i] << " ";
cout << endl;
return ;
}
//u没被选择
dfs(u + 1);
//u被选择
v.push_back(u);
dfs(u + 1);
//还原现场
v.pop_back();
}
int main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin >> n;
dfs(1);
return 0;
}
方法三:纯位运算
思路:进行一次循环列举所有选择方案。应注意的是,二进制应逆序,否则得不到答案。
时间复杂度估算:O(n * 2^n)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
int n;
int main()
{
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin >> n;
for(int i = 0; i < 1 << n; i++)
{
for(int j = 0; j < n; j++)
if(i >> (n - j - 1) & 1)
cout << j + 1 << " ";
cout << endl;
}
return 0;
}