姓名: 徐熙喆
赛道: 基础组
类型: 算法技能
类型:dfs(深搜)的思路。
思路部分
首先,dfs 分为 4 个大部分。分别是抵达目标,枚举前进方向与判断,搜索部分和回溯。
抵达目标
顾名思义,搜索目标就是抵达边界,当你到达一个边界,就是到达终点,你要干一件事。具体是什么取决于题目。
3 个大的版块:
一、判断能不能到达终点,能到达输出 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 );
定义变量:
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) 这个坐标,所以,棋盘外的东西也是不能走的。
- 能不能走:我们基本上是满足一个条件就不能走了,这里可以是步数用光了,也可以是墙。
难点
模版
void dfs(int x,int y,int bs){
if(x==n&&y==m){
//抵达边界
}
for(int i=0;i<4;i++){//枚举
//预冥想
//灵魂三连问(能不能走)
//递归与回溯
}
}