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
解題思路
這個題跟解決全排列是一樣的思路。先放置一個數,然後在沒有放置過的數字當中選擇一個放在下一個位置,依次進行。依舊是用遞歸解決,在選擇放置下一個數字時,和解決之前的問題是一樣的,還是要在沒有放置過的數字當中選一個放置,這樣就是把問題分解成爲了規模更小的相同的問題。
跟全排列有兩點不一樣的地方
- 遞歸終止條件是已經放置了 r 個數字
- 舉個例子,比如說 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);
}