今天我们来聊聊关于 dfs(深搜)的思路。
思路部分
首先,dfs 分为 4 个大部分。分别是抵达目标,枚举前进方向与判断,搜索部分和回溯。
抵达目标
顾名思义,搜索目标就是抵达边界,当你到达一个边界,就是到达终点,你要干一件事。具体是什么取决于题目。
3 个大的版块:
判断能不能到达终点,能到达输出 Yes,不能到达输出 No。这时,到达终点(抵达边界)我们要做的是就是输出 Yes 因为到达了终点,最后用 exit(0) 强制结束程序(因为已经输出了 Yes 程序结束了)。
计算到达方法,这是我们要做的事是 ans++ 因为我们到达了终点,所以这条路可行(又多了一条可行的路),所以 ans++,不用担心重复问题,因为让 i++ 的 for 循环不会 i-- 也就是搜索原来的已经搜索过的路径。
计算最短或最多路径,没路输出 -1,这时又有两种情况(有很多种方法中的一种,最好理解的):
最短:
定义变量 mina 不是 min,这里应该都知道,因为 min 已经是被定义的函数了。首先将初始值设为 最大值+1 普遍都是 n * m + 1,为什么要 +1 呢,因为 n \times m 是可以达到的,就不能使后面的输出 -1 的判断成立,因为输出 -1 是因为没有路径可达到,所以输出的 -1,话说刚才, n \times m 是可以达到的,而 n \times m + 1 是不可以达到的,所以定义 mina 的程序是:int mina=n*m+1;,而到达边界更新的程序是 mina=min(mina,ans),如果 mina 最后没变,代表没有路径可达到,输出 -1。
最长:
定义变量 maxa 不是 max,这里应该都知道,因为 max 已经是被定义的函数了。首先将初始值设为 -1,简单解释成:让他每次有新的路径就能更新,而没人可以走 -1 步,所以初始值设为 -1,而到达边界更新的程序是 maxa=max(maxa,ans),如果 maxa 最后没变,代表没有路径可达到,输出 -1。
枚举前进方向与判断
这里只讲普遍的题目(四个方向的)。
搜索和递归差不多,都是拆分成小的部分,这里小的部分是单个坐标。
四个方向分别是:
- 上,
dx-1,dy(行数-1); - 下,
dx+1,dy(行数+1); - 左,
dx-1,dy(列数-1); - 右,
dx-1,dy(列数+1);
定义变量:
int dx[4]={-1,1,0,0};
int dy[4]={0,0,-1,1};
因为是枚举,我们把每个方向都试一遍,用 for 循环:
for(int i=0;i<4;i++){;}
因为变量的下标是是从 0 开始的,所以 for 循环为 0 ~ 3。
判断:
先预冥想一下下一个的坐标:
int xx=x+d[i],dy=y+d[y];
灵魂三连问(三个不能走的地方),这里将简单点:
- 边界,超过边界不能走。
if(xx<1||yy<1||xx>n||yy>m)return ;。
- 墙不能走。
if(c[xx][yy]=='#")return ;。
- 走过不能走。
if(vis[xx][yy]==1) return ;。
剩下的就是能走的。
搜索部分
续接上一个部分的思路:“剩下的就是能走的。”
因为能走了,我们可以让他走了,但是要将 vis 变成 1 因为他走过了,要打标记。
vis[xx][yy]=1。
接下来让他走。
dfs(xx,yy);
因为这个地方可以走。
回溯
回溯是因为实在走到了死胡同,走不了了,但是有的程序最好不加回溯,在这里我们来讨论一下:
因为不走了,所以抹除标记,抹除所有的记录,因为根本就没走过。
vis[xx][yy]=0。
因为有些题目有步数:
又是两种情况:
- 全局变量,自己写回溯。
- 形式参数,自带回溯,不用写。
到这里整个 dfs 部分就讲完了,最后是模版。
模版
void dfs(int x,int y,int bs){
if(x==n&&y==m){
//抵达边界
}
for(int i=0;i<4;i++){//枚举
//预冥想
//灵魂三连问(能不能走)
//递归与回溯
}
}