相信很多人对dfs什么时候需要回溯什么时候不需要回溯很模糊,我一开始也是这样的。
DFS的本质是每一步做选择,当这个选择可做可不做(比如迷宫,这一步可以走,也可以不走),要在递归之后回溯。如果状态用参数传递的话,也可以直接改变参数,这一步相当于回溯(因为调用时没有改变原有参数的值)。
当遇到一个选择时,一定要对它操作时,那么就不需要回溯。比如要标记求所有情况,找到了就要标记。如果取消标记,那么就会重复计算。
拿迷宫与我今天做的题目填涂颜色举例。
迷宫题
填涂颜色
这两个题目都是可以用dfs来做,而迷宫题目是一定要用到回溯的用法的而填涂颜色是不需要用的。因为迷宫题目是要求几条路径假如你不回溯的话你的路径可能会少。因为你探索过程把探索的路途进行了标记如果你没有把标记去除就回到上一个岔路口,如果另外一个岔路又刚刚好能连通你之前所走过的路那么这时就会形成堵塞到达不了终点也就少一条路。
而填涂问题它是只是需要吧相连通的路进行标记就行,不需要去计算有多少条路而假如用了回溯的话有些相连的路又变成没被标记就会造成结果错误。
现在说说填涂问题的思路
我是基于此图再向外阔一层为0的数,这样一来dfs时就不再需要从多个点出发了这样它从0,0出发就遍历全部外围为0的数。先把围墙标记,然后再遍历外围为0的数并标记这样一来就只有里面为0的数未被标记,直接根据此条件进行打印就行记住要把范围扩回来。代码如下
#include <stdio.h>
int k[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
int n, book[35][35], a[35][35];
void dfs(int x, int y) {
if (x > n+1 || x < 0 || y > n+1 || y < 0) {
return;
}
if (book[x][y] == 1 || a[x][y] == 1) {
return;
}
book[x][y] = 1;
for (int i = 0; i < 4; i++) {
int tx = x + k[i][0];
int ty = y + k[i][1];
dfs(tx, ty);
}
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
scanf("%d", &a[i][j]);
}
}
dfs(0, 0);
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
if (a[i][j] == 0 && book[i][j] == 0) {
a[i][j] = 2;
}
printf("%d ", a[i][j]);
}
printf("\n");
}
return 0;
}
找水坑问题:
和上面一题目类似但又有区别。首先应该先找到有水且没有被表记然后从此开始遍历注意是四面八方遍历直到遇到完全没有水为止。然后再寻找一处水且未被标记的循环。当重新找到一个还未被标记的水时水坑就加1刚开时为0.最后就直接输出就行。
代码如下
#include<stdio.h>
int p[8][2]={{0,1},{1,0},{-1,0},{0,-1},{1,1},{1,-1},{-1,1},{-1,-1}};
char a[105][105];
int book[105][105],n,m,cont=0;
void dfs(int x,int y)
{
int tx,ty;
for(int i=0;i<8;i++)
{
tx=x+p[i][0];
ty=y+p[i][1];
if(a[tx][ty]=='W'&&book[tx][ty]==0)
{
book[tx][ty]=1;
dfs(tx,ty);
}
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
scanf(" %c",&a[i][j]);
}
}
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
if(a[i][j]=='W'&&book[i][j]==0)
{
dfs(i,j);
cont++;
}
}
}
printf("%d",cont);
return 0;
}
对于广搜类型的题目如今我还是未能完全理解希望之后能再进一步把。