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*10^6^),可以接受。

我一开始的代码出了许多问题,经过我的辛苦努力,最后还是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),样例过不了吓得我认为自己连例题都要借鉴题解)

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

猜你喜欢

转载自www.cnblogs.com/Wild-Donkey/p/12234332.html