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);
}  

猜你喜欢

转载自blog.csdn.net/weixin_43966538/article/details/106319190