DFS总结

DFS总结

DFS介绍

DFS(Depth First Search)是一种搜索方式,时间复杂度很高,适用小数据,原理是自己调用自己。
DFS实质上是递归,包括四要素:类型,参数列表,边界,调用。
以下面一个为引例:

long long/*类型*/j(int x)/*参数列表*/{
	if(x==1) return 1;//边界
	return j(x-1)*x;//调用
}

这实现了求 x x x 阶乘 x ! = 1 × 2 × ⋯ × x x!=1 \times 2 \times \dots \times x x!=1×2××x
示例:求 3 3 3 阶乘 :
∵ j ( x ) = j ( x − 1 ) × x , j ( 1 ) = 1 \because j(x)=j(x-1) \times x,j(1)=1 j(x)=j(x1)×x,j(1)=1
∴ j ( 3 ) = j ( 3 − 1 ) × 3 = j ( 2 ) × 3 = j ( 2 − 1 ) × 2 × 3 = j ( 1 ) × 2 × 3 = 1 × 2 × 3 = 6 \therefore j(3)=j(3-1) \times 3=j(2) \times 3=j(2-1) \times 2 \times 3=j(1) \times 2 \times 3=1 \times 2 \times 3=6 j(3)=j(31)×3=j(2)×3=j(21)×2×3=j(1)×2×3=1×2×3=6
∴ j ( 3 ) = 6 \therefore j(3)=6 j(3)=6
递归有时也很快,如求两个数的最大公约数:

int gcd(int x,int y){
	if(y==0) return x;
	return gcd(y,x%y);
}

通过辗转相除法很快地求两个数的最大公约数。

枚举方式

DFS用一句话概括就是:“一直往下走,走不通回头,换条路再走,直到无路可走”。
枚举方式有三种:

  1. 子集枚举
  2. 排列枚举
  3. 组合枚举

子集枚举

子集枚举即枚举集合中的每个子集。
每个数有选或不选两种状态,所以时间复杂度为 O ( 2 n ) O(2^n) O(2n)

书架

就是枚举子集,求和,与 b b b 比较,得出答案。
代码如下:

int n,vis[33]={},a[33]={},ans=inf,b;//vis为该数选或不选,a为集合
void dfs(int k){
	if(k==n+1){
		int sum=0;
		for(int i=1;i<=n;i++) sum+=vis[i]*a[i];
		if(sum>=b) ans=min(ans,sum-b);//更新答案
		return;
	}for(int i=0;i<=1;i++){
		vis[k]=i;
		dfs(k+1);
	}
}
示例输入
5 16
3
1
3
5
6
示例输出
1
样例解释

子集 { 3 , 3 , 5 , 6 } \{3,3,5,6\} {3,3,5,6} 和为 17 17 17,比 16 16 16 大且比 16 16 16 1 1 1

排列枚举

一组数据的一种排序,叫这组数据中一个排列。
一组数据( n n n 个数)有 A n n = n ! A_n^n=n! Ann=n! 个全排列,所以时间复杂度为 O ( n ! ) O(n!) O(n!)

递归实现全排列枚举

int n,a[33]={},ans=0,vis[33]={};//a表示全排列,vis表示有没有选
void dfs(int k)/*k表示一个全排列的一个数据*/{
	if(k==n+1){
		for(int i=1;i<=n;i++) cout<<a[i]<<' ';
		cout<<'\n';
		return;
	}for(int i=1;i<=n;i++){
		if(vis[i]==0){
			vis[i]=1;//标记
			a[k]=i;//设置
			dfs(k+1);
			vis[i]=0;//回溯
			a[k]=0;
		}
	}
}
示例输入
3
示例输出
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1

c++有一个内置的求上一个全排列的函数prev_permutation,和求下一个全排列的函数next_permutation
这可以秒过本题,代码不再堆了。

洛谷P1008三连击

链接
全排列枚举 { 1 , 2 , … , 9 } \{1,2,\dots,9\} {1,2,,9},然后每三位一个数,并判断。
设排到 { 1 , 9 , 2 , 3 , 8 , 4 , 5 , 7 , 6 } \{1,9,2,3,8,4,5,7,6\} {1,9,2,3,8,4,5,7,6},则三数为 192 , 384 , 576 192,384,576 192,384,576,因为 192 : 384 : 576 = 1 : 2 : 3 192:384:576=1:2:3 192:384:576=1:2:3,所以这是一组合法序列,输出这三个数。
代码如下:

#include<bits/stdc++.h>
using namespace std;
int a[10]={0,1,2,3,4,5,6,7,8,9};
int main(){
	do{
		int x=a[1]*100+a[2]*10+a[3];//第一个数
		int y=a[4]*100+a[5]*10+a[6];//第二个数
		int z=a[7]*100+a[8]*10+a[9];//第三个数
		if(x==y/2&&x==z/3) cout<<x<<' '<<y<<' '<<z<<endl;//判断
	}while(next_permutation(a+1,a+1+9));//全排列函数
	return 0;
}

这个代码必定输出:

192 384 576
219 438 657
273 546 819
327 654 981

组合枚举

就是在 n n n 个数中挑选 m m m 个数,时间复杂度为 O ( C m n ) O(C_m^n) O(Cmn)

组合型枚举

1 1 1 n n n n n n 个整数中随机选出 m m m 个,首先,同一行内的数升序排列,相邻两个数用一个空格隔开。其次,按字典序输出
代码如下:

int n,m,vis[33]={},num[33]={};
void dfs(int k,int mini){
	if(k==m+1){
		for(int i=1;i<=m;i++){
			cout<<num[i]<<' ';
		}cout<<'\n';return;
	}for(int i=mini;i<=n;i++)/*因为按照升序排列,所以新设置一个参数mini*/{
		if(vis[i]==0){
			vis[i]=1;//标记
			num[k]=i;//设置
			dfs(k+1,i);//mini设为i,所以一定是升序排列
			vis[i]=0;//回溯
		}
	}
}
示例输入
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

这次总结到此结束!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值