问题 B: 【递归入门】组合的输出
时间限制: 1 Sec 内存限制: 128 MB
提交: 740 解决: 436
[提交][状态][讨论版][命题人:外部导入]
题目描述
排列与组合是常用的数学方法,其中组合就是从n个元素中抽出r个元素(不分顺序且r < = n),我们可以简单地将n个元素理解为自然数1,2,…,n,从中任取r个数。
现要求你不用递归的方法输出所有组合。
例如n = 5 ,r = 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
输入
一行两个自然数n、r ( 1 < n < 21,1 < = r < = n )。
输出
所有的组合,每一个组合占一行且其中的元素按由小到大的顺序排列,所有的组合也按字典顺序。
法1(散列版):
参考 优快云博主Tibetshun 的文章
思路:
用两个数组,一个存数,一个用来存数的情况;
刚开始a中1 2 3 4 5 对应的hash 1 1 1 0 0 输出1 2 3,对hash进行prev_permutation(),不断的得到hash的上一个排列,在此过程中不断按要求输出即可。
prev_permutation是C++中的库函数,用以获得当前排列的上一个排列 比如111000的上一个排列是11010,以此类推,直到00111 即345结束。
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=100010;
int main() {
int hash[maxn]={0};
int n,r;
int a[maxn];
cin>>n>>r;
for(int i=0;i<n;i++) a[i]=i+1;
for(int i=0;i<r;i++) hash[i]=1;
do{
for(int i=0;i<n;i++){
if(hash[i])
cout<<a[i]<<" ";
}
cout<<endl;
}while(prev_permutation(hash,hash+n));
return 0;
}
法2(递归版):
#include<iostream>
#include<cstdio>
#include<string>
#include<queue>
#include<stack>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn=100010;
int n,r;
int a[maxn];
bool hashTable[maxn]={false};
vector<int> ans;
void dfs(){
if(ans.size()==r+1){
for(int i=1;i<ans.size();i++){
if(i!=1) cout<<" ";
cout<<ans[i];
}
cout<<endl;
return ;
}
// cout<<"hr"<<endl;
for(int i=0;i<n;i++){//每次放第i位 都是从所有数中选满足条件的数
if(hashTable[a[i]]==false&&a[i]>ans.back()){
hashTable[a[i]]=true;
ans.push_back(a[i]);
dfs();
ans.pop_back();
hashTable[a[i]]=false;
}
}//循环结束即为第i位的各种情况已经讨论过,之后返回上层递归,讨论i-1位
}
int main(){
cin>>n>>r;
for(int i=0;i<n;i++){
a[i]=i+1;
}
ans.push_back(-1);
dfs();
return 0;
}
法3(非递归版):
参考qq_20679687的博客
思路:模拟进制的思想,且根据题意隐藏的条件对“进制”的各位的最大取值进行了限制。
样例的各位与各位最大取值 :r-2 r-1 r 位分别为 n-2 n-1 n,第3位为5时向第二位进位,第2位为4时向第一位进位,当第一位为3时表示已完成题目要求,退出循环。每次进入next_combination()的时候先判断需不需要进位,不需要的话第r位+1即可,需要进位的话,先判断出来是第idx位需要进位,然后让idx位数+1,注意此时的第idx位一定是不满的,所以不需要再判断。
#include<iostream>
#include<cstdio>
#include<string>
#include<queue>
#include<stack>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn=100010;
int n,r,a[maxn];
bool next_combination(){
int i=0;
while(a[r-i]==n-i&&r-i>0){
i++;
}//从最后一位往前找哪一位需要进位
if(r-i==0) return false; //如果溢出
a[r-i]++;//进位
while(i>=0){
a[r-i+1]=a[r-i]+1;
i--;
}
return true;
}
int main(){
while(cin>>n>>r){
for(int i=1;i<=r;i++) a[i]=i;
do{
for(int i=1;i<=r;i++)
cout<<a[i]<<" ";
cout<<endl;
}while(next_combination());
}
return 0;
}