ybt1317 组合方案(dfs经典例题)超硬核

ybt1317 组合输出

dfs(深搜)例题

【题目描述】

排列与组合是常用的数学方法,其中组合就是从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)。

【输出】

所有的组合,每一个组合占一行且其中的元素按由小到大的顺序排列,每个元素占三个字符的位置,所有的组合也按字典顺序。

【输入样例】

5 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<n<21,根据组合公式:

\[C^r_n=\frac {n!}{r!(n-r)!} \]

可知,如果n取最大值20,那么方案数将是:

\[\frac {2432902008176640000}{r!(20-r)!} \]

画出图像后,发现图像过r=10对称,r=10时取到最大值184756。

每个方案会有最多19个元素,共有19*184756=3510364个元素。

dfs的复杂度为大约O(3.5*106),可以接受。

我一开始的代码出了许多问题,经过我的辛苦努力,最后还是AC了,看来想算法远不如实现算法困难:

#include<iostream>
#include<cstdio>
using namespace std;
int n,r,ans[30];
void dfs(int at,int x) {//at表示当前要判断的位,x表示上一位的数字(这一位的数字要从x之后找)
	for(int i=x+1;i<=n;i++) {//枚举at位所有可行的数字
		ans[at]=i;//赋值
		if(at==r) {//位数已经足够,可以输出
			for(int j=1;j<=r;j++){
				printf("%3d",ans[j]);//格式
			}
			cout<<endl; 
		}
		else{
			dfs(at+1,i);//当前位不是最后一位,继续dfs,以i为当前位,也就是at+1位的上一位
		}
	}
	return;
}
int main() {
	cin>>n>>r;
	dfs(1,0);//共同的起点:当前位是第1位,上一位(第零位)值是0
	return 0;
}

(本来调试时把第10行printf里的ans[j]打成ans[i],并且把第22行的dfs(1,0)打成dfs(0,0),样例过不了吓得我认为自己连例题都要借鉴题解)

但是我终于自己打出来此题,祝贺一下!!

posted @ 2020-01-26 16:09  Wild_Donkey  阅读(348)  评论(0编辑  收藏  举报