深度搜索
DFS的空间复杂度为 O(h) h表示深入的层数,相较于BFS,其空间复杂度较好,但是时间复杂度较差
DFS的三种类型
分别为指数型,全排列,组合型
指数型搜索
例题:从1-n中随机选取任意多个数,输出所有可能的选择方案
输入格式:输入一个整数 n
输出格式:输出每一种方案,对于没有任何数的方案直接输出空行
数据范围:1<=n<=15
输入样例:3
输出样例:
(空行)
3
2
2 3
1
1 3
1 2
1 2 3
代码如下:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n;
ll st[15];//表示这个位置的数的状态,0表示未知,1表示已选,2表示不选
void dfs(ll x)
{
if(x>n)
{
for(int i=1;i<=n;i++)
{
if(st[i]==1)
{
cout<<i<<" ";
}
}
cout<<"\n";
return ;
}
//不选
st[x]=2;
dfs(x+1);
st[x]=0;
//选
st[x]=1;
dfs(x+1);
st[x]=0;
}
int main()
{
cin>>n;
dfs(1);
return 0;
}
全排列
例题:全排列问题
AC代码(1)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MAXN = 1e5+5;
ll n;
ll flag[10];//标记当前数是否被搜索过
ll a[10];//当前位置放的数
void dfs(ll u)
{
if(u==n+1)
{
for(int i=1;i<=n;i++)
{
printf("%5lld",a[i]);
}
printf("\n");
}
for(int i=1;i<=n;i++)
{
if(!flag[i])
{
a[u]=i;
flag[i]=1;
dfs(u+1);
flag[i]=0;
}
}
}
int main()
{
cin>>n;
dfs(1);
return 0;
}
AC代码(2)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n;
bool a[15];
ll op[15];
void dfs(ll x)
{
if(x>n)
{
for(int i=1;i<=n;i++)
{
printf("%4d",op[i]);
}
cout<<endl;
return ;
}
for(int i=1;i<=n;i++)
{
if(!a[i])
{
a[i]=1;
op[x]=i;
dfs(x+1);
a[i]=0;//回溯
op[x]=0;//回溯 ,且此处可以省略,因为值会被后面直接替换。
}
}
}
int main()
{
cin >> n;
dfs(1);
return 0;
}
组合型
例题:组合的输出
AC代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,r;
ll a[25];
void dfs(ll x,ll st)
{
if(x>r)
{
for(int i=1;i<=r;i++)
{
cout<<setw(3)<<a[i];
}
cout<<"\n";
return ;
}
for(int i=st;i<=n;i++)
{
a[x]=i;
dfs(x+1,i+1);
a[x]=0;
}
}
int main()
{
cin>>n>>r;
dfs(1,1);
return 0;
}
关于个人对以上三种类型的DFS的理解:
指数搜索型是在一堆数中可以取任意数,可以理解为,一个位置,我可以选任何一个数字放在这个位置上。
全排列也是在一堆数中选数,但是数字之间不能有重复。也就是说,一个位置我取了3这个数,别的位置就不能取3 这个数了,而指数型则可以取。
组合型与全排列比较相似,全排列是选中所有,而组合是所有中取部分。
举个例子:一共有五个数,三个位置,这三个位置只能放五个中的三个,高中学过。
以上三者在用代码实现的过程中,需要根据条件对不满足条件的部分进行剪枝,以此来减少计算。
DFS算法在使用之前,建议先判断其属于哪种搜索类型,然和再根据搜索类型画出搜索的全流程图,以方便你对DFS的过程的理解。
DFS模板
int dfs(int t)
{
if(满足输出条件)
{
输出解;
}
else
{
for(int i=1;i<=尝试方法数;i++)
if(满足进一步搜索条件)
{
为进一步搜索所需要的状态打上标记;
dfs(t+1);
恢复到打标记前的状态;//也就是说的{回溯一步}
}
}
}
例题:迷宫
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,m,t;
ll sx,sy,fx,fy;
ll x,y;
ll dx[4]={0,0,1,-1};
ll dy[4]={1,-1,0,0};
bool flag[10][10];
ll a[10][10];
ll ans;
void dfs(ll tx,ll ty)
{
if(tx == fx && ty == fy)
{
ans++;
return ;
}
else
{
for(int i=0;i<4;i++)
{
if(flag[tx+dx[i]][ty+dy[i]]==0 && a[tx+dx[i]][ty+dy[i]]==1)
{
flag[tx][ty]=1;
dfs(tx+dx[i],ty+dy[i]);
flag[tx][ty]=0;
}
}
}
}
int main()
{
cin>>n>>m>>t;
cin>>sx>>sy>>fx>>fy;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
a[i][j]=1;
}
while(t--)
{
cin>>x>>y;
a[x][y]=0;
}
dfs(sx,sy);
cout<<ans;
return 0;
}
在DFS的使用中,往往会出现计算重复的情况,这种情况一般可以先预处理,将已经计算好的答案存到一个数组中来解决,可大幅降低时间复杂度,从而做到处理一些数据过大的情况
接下来请看实战运用
eg:
优化前的代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MAXN =1e3;
ll n;
ll a[MAXN];
ll b[MAXN]={6,2,5,5,4,5,6,3,7,6};
ll ans;
ll col(ll x)
{
if(b[x]) return b[x];
else
{
ll sum=0;
while(x)
{
sum+=b[x%10];
x/=10;
}
return sum;
}
}
void dfs(ll x,ll cnt)//x表示第几个位置的数,一共有三个位置 , cnt表示已使用的火柴数
{
if(cnt>n) return ;
if(x>3)
{
// cout<<a[x]<<" ";
if(a[1]+a[2]==a[3]&&cnt==n)
{
ans++;
}
return ;
}
for(int i=0;i<=1000;i++)
{
a[x]=i;
dfs(x+1,cnt+col(i));
a[x]=0;
}
}
int main()
{
cin>>n;
n-=4;
dfs(1,0);
cout<<ans;
return 0;
}
通过所耗时间如下图:
优化后的代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MAXN =1e3;
ll n;
ll a[MAXN];
ll b[MAXN]={6,2,5,5,4,5,6,3,7,6};
ll ans;
//ll col(ll x)
//{
// if(b[x]) return b[x];
// else
// {
// ll sum=0;
// while(x)
// {
// sum+=b[x%10];
// x/=10;
// }
// return sum;
// }
//}
void dfs(ll x,ll cnt)//x表示第几个位置的数,一共有三个位置 , cnt表示已使用的火柴数
{
if(cnt>n) return ;
if(x>3)
{
// cout<<a[x]<<" ";
if(a[1]+a[2]==a[3]&&cnt==n)
{
ans++;
}
return ;
}
for(int i=0;i<=1000;i++)
{
a[x]=i;
// dfs(x+1,cnt+col(i));
dfs(x+1,cnt+b[i]);
a[x]=0;
}
}
int main()
{
cin>>n;
n-=4;
for(int i=10;i<=1000;i++)
{
b[i]=b[i%10]+b[i/10];//这步属于优化部分,避免重复计算
}
dfs(1,0);
cout<<ans;
return 0;
}
通过所需时间如下图:
指数型搜索实战例题:
地图类问题实战例题:
连通器问题实战例题:(个人理解:先遍历,找到后再搜)
DFS例题:
acwing 821 跳台阶题解dfs版
//DFS普通版
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n;
ll ans;
ll dfs(ll x)//x表示目前处于第几个台阶
{
if(x==1) return 1;
if(x==2) return 2;
else return dfs(x-1)+dfs(x-2);
}
int main()
{
cin>>n;
ans=dfs(n);
cout<<ans;
return 0;
}
//DFS记忆化搜索版
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n;
ll ans;
ll mem[30];
ll dfs(ll x)//x表示目前处于第几个台阶
{
if(mem[x]) return mem[x];
ll sum=0;
if(x==1) sum = 1;
else if(x==2) sum = 2;
else sum = dfs(x-1)+dfs(x-2);
mem[x]=sum;
return sum;
}
int main()
{
cin>>n;
ans=dfs(n);
cout<<ans;
return 0;
}
P1683 DFS AC题解
之前用大家都用BFS写过这道题,现在可以换DFS试试
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll w,h;
char s[25][25];//图
ll sx,sy;//记录初始横纵坐标
ll dx[4]={1,-1,0,0};//位移向量
ll dy[4]={0,0,1,-1};
ll ans;//记录格子数
bool st[25][25];//记录格子的状态,0为尚未走过,1为走过
void dfs(ll x,ll y)
{
for(int i=0;i<4;i++)
{
ll tx=x+dx[i];
ll ty=y+dy[i];
if(tx<0||tx>h||ty<0||ty>w) continue;
if(s[x+dx[i]][y+dy[i]]!='.') continue;
if(st[tx][ty]) continue;
st[tx][ty]=1;
ans++;
dfs(tx,ty);
}
}
int main()
{
cin>>w>>h;//此处题目有坑
for(int i=0;i<h;i++)
{
cin>>s[i];
}
for(int i=0;i<h;i++)
for(int j=0;j<w;j++)
{
if(s[i][j]=='@')
{
sx=i;
sy=j;
st[sx][sy]=1;
dfs(sx,sy);
}
}
ans++;//第一次也要算在内
cout<<ans;
return 0;
}
dfs记忆化搜索经典例题:01背包问题 题解:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 1010;
ll n,m;
ll v[N],w[N];
ll res;
ll mem[N][N];
// x表示从第x物品开始,spV表示当前背包剩余容量,sumW表示当前背包装的所有物品的总价值
ll dfs(ll x,ll spV)
{
if(mem[x][spV]) return mem[x][spV];
ll sum=0;
if(x>n) sum = 0;
// if(sumW > res) res = sumW;
else if(spV < v[x])
{
// 因为体积不够,只能不选
sum = dfs(x+1,spV);
}
else if(spV >= v[x])//当剩余容量大于当前物品的体积
{
//就有 选 和 不选 两种子问题
sum = max(dfs(x+1,spV),dfs(x+1,spV - v[x])+ w[x]);
}
mem[x][spV] = sum;
return sum;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>v[i]>>w[i];
}
res = dfs(1,m);
cout<<res;
return 0;
}
dfs记忆化搜索过程图
14届蓝桥杯 D题 飞机降落
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll t,n,a[15],b[15],c[15];
bool st[15];
bool sp ;
void dfs(ll x,ll t)//x表示第几架飞机起飞
{
if(sp) return ;
if(x>n)
{
sp = true;
return ;
}
for(int i=1;i<=n;i++)
{
if(!st[i])
{
if(b[i] + a[i] >= t)
{
st[i] = true;
if(a[i]>t)
dfs(x+1,a[i]+c[i]);
else
dfs(x+1,t+c[i]);
st[i] = false;
}
else return ;
}
}
}
int main()
{
cin>>t;
while(t--)
{
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i]>>b[i]>>c[i];
for(int i=0;i<=15;i++)
st[i] = false;
sp = false;
dfs(1,0);
if(sp) cout<<"YES"<<"\n";
else cout<<"NO"<<"\n";
}
return 0;
}