DFS

问题 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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值