用DFS求组合
先看题目:
排列与组合是常用的数学方法,其中组合就是从n个元素中抽出r个元素(不分顺序且r≤n),我们可以简单地将n个元素理解为自然数1,2,…,n,从中任取r个数。
现要求你输出所有组合。
例如n=5,r=3,所有组合为:
123,124,125,134,135,145,234,235,245,345;
再上题解:无回溯递归DFS
#include<stdio.h>
int n=0,r=0;
int a[25],b[20000];
int i=0,j=0,k=0,t=0,t1=0,t2=0;
void dfs(int i,int k)
{
if(i==n+1)
return;
i++;
//printf("i:%d t:%d k:%d\n",i,t,k);//用于检验
if(k==r)//位数达到时输出且返回上层
{for(j=0;j<r;j++)
{printf("%3d",a[j]);}
printf("\n");
return;
}
a[k]=i;//存入数组
dfs(i,k+1);//选入,此处i不必加入参数
dfs(i,k);//不选
}
int main()
{
scanf("%d %d",&n,&r);
dfs(0,0);
return 0;
}
原理:
n<21给了很大的操作空间
用DFS递归,每个数都有两个选择:选入组合or不选入;
这种方法的好处就是不存在回溯问题,缺点是耗时大,由图可知,这种方法其实给出了x位数不同组合的可能,哪怕一个都不选最后都包括在内。从第一位开始没一位都有两种可能,不考虑位数限制时间复杂度应为2^n,在n<21时可以放心使用(且不用担心顺序问题)。
举一个反例
从某种角度来说这题依然是可以用组合的方法解决(n个数求其为2的组合)
int abss(long long g,long long h)
{if(g>=h)
return g-h;
else
return h-g;
}
void dfs(long long i,long long k)
{if(i>n)
return;
i++;
//printf("i:%lld k:%lld\n",i,k);
if(k==2)
{
j=abss(b[1],b[0]);
//printf("%lld %lld\n",b[0],b[1]);
if(j==c)
sum++;
return;
}
b[k]=a[i];
dfs(i,k+1);
dfs(i,k);
}
修改条件后打版提交在三个test中TL
所以说,这种方法在数据量大时容易直接暴毙
包括背包问题,若是我们能列举所有组合可能,自然问题迎刃而解,但由于时间限制不允许很多时候只能DP,或者用带回溯DFS。
排列问题也是同理,需要带回溯DFS。
(两道例题来自洛谷。)