dfs解決組合問題

dfs解決組合問題

題目鏈接

題目概述

組合就是從n個元素中抽出r個元素(不分順序且 r <= n ),我們可以簡單地將n個元素理解爲自然數1,2,…,n,從中任取r個數。

輸入: 一行兩個自然數 n、r ( 1 < n < 21,1 < = r < = n )。
輸出: 所有的組合,每一個組合佔一行且其中的元素按由小到大的順序排列,所有的組合也按字典順序。

例如 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

解題思路

這個題跟解決全排列是一樣的思路。先放置一個數,然後在沒有放置過的數字當中選擇一個放在下一個位置,依次進行。依舊是用遞歸解決,在選擇放置下一個數字時,和解決之前的問題是一樣的,還是要在沒有放置過的數字當中選一個放置,這樣就是把問題分解成爲了規模更小的相同的問題。

跟全排列有兩點不一樣的地方

  1. 遞歸終止條件是已經放置了 r 個數字
  2. 舉個例子,比如說 n = 5,r = 3,找到了(1,3,4)這個組合,如果還按之前循環的方式,在 1 到 n 中找未放置過的數字,標記後繼續遞歸,但是我們思考一下,在之後是會找到(3,1,4)或者(4,1,3)等組合,所以我們要保證不會出現這樣元素重複的組合,所以在循環時,就要在大於之前放置的數字的中尋找,這樣既可以保證一個組合中數字是從小到大排列的,也可以保證不會出現元素重複的組合。

代碼如下

#include<cstdio>
#include<cstring> 
#include<algorithm>
using namespace std;

int n,r;
int a[5];
bool vis[23];
void dfs(int x) {
	if(x == r+1) { //結束的條件是 已經放置了r的數字。此時是r+1;
		for(int i = 1; i<= r; i++) {
			printf("%d ",a[i]);
		}
		printf("\n");
		return;
	}
	for(int i = a[x-1]+1; i<= n; i++) { //前一個放置的數字爲a[x-1],之後的查找在a[x-1]到n中進行。
		if(vis[i] == false) {
			a[x] = i;
			vis[i] = true;
			dfs(x+1);
			vis[i] = false;
		}
	}
	return;
}
int main() {
	scanf("%d%d",&n,&r);
	memset(vis,false,sizeof(vis));
	a[0] = 0;
	dfs(1);
}  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章