dfs 搜索讲解
Fubaogege
·
·
学习·文化课
今天我们来聊聊关于 dfs(深搜)的思路。
思路部分
首先,dfs 分为 4 个大部分。分别是抵达目标,枚举前进方向与判断,搜索部分和回溯。
抵达目标
顾名思义,搜索目标就是抵达边界,当你到达一个边界,就是到达终点,你要干一件事。具体是什么取决于题目。
判断能不能到达终点,能到达输出 `Yes`,不能到达输出 `No`。这时,到达终点(抵达边界)我们要做的是就是输出 `Yes` 因为到达了终点,最后用 `exit(0)` 强制结束程序(因为已经输出了 `Yes` 程序结束了)。
计算到达方法,这是我们要做的事是 `ans++` 因为我们到达了终点,所以这条路可行(又多了一条可行的路),所以 `ans++`,不用担心重复问题,因为让 `i++` 的 `for` 循环不会 `i--` 也就是搜索原来的已经搜索过的路径。
计算最短或最多路径,没路输出 `-1`,这时又有两种情况(有很多种方法中的一种,最好理解的):
最短:
定义变量 `mina` 不是 `min`,这里应该都知道,因为 `min` 已经是被定义的函数了。首先将初始值设为 `最大值+1` 普遍都是 $n \times 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$);
定义变量:
```c
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 \sim 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 部分就讲完了,最后是重点难点和模版。
## 重点
一:“灵魂三连问”的用不用:
我们已知,灵魂三连问的具体的是指:**走没走过**,**到没到边界**和**能不能走**。
- **走没走过**:我们通常遇到的题目都是只能走一次,求最短路径,所以不能重复的走。
- **到没到边界**:就以我们最具代表性的**棋盘问题**来讲,棋盘是有边界的,在一个 $11\times11$ 的棋盘里我们不能走到 $(12,12)$ 这个坐标,所以,棋盘外的东西也是不能走的。
- **能不能走**:我们基本上是满足一个条件就不能走了,这里可以是步数用光了,也可以是墙。
## 模版
```c
void dfs(int x,int y,int bs){
if(x==n&&y==m){
//抵达边界
}
for(int i=0;i<4;i++){//枚举
//预冥想
//灵魂三连问(能不能走)
//递归与回溯
}
}
```