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),样例过不了吓得我认为自己连例题都要抄借鉴题解)
但是我终于自己打出来此题,祝贺一下!!