什么时候需要回溯:

DFS的本质是每一步做选择,当这个选择可做可不做(比如迷宫,这一步可以走,也可以不走),要在递归之后回溯。

如果状态用参数传递的话,也可以直接改变参数,这一步相当于回溯(因为调用时没有改变原有参数的值)

dfs(x,y):
   vis(x,y);
   dfs(x+1,y);
   撤销vis(x,y);

等价于

dfs(x,y):
   vis(x,y)
   x++;
   dfs(x,y);
   x--;
   撤销vis(x,y);

如迷宫问题

【DFS笔记】什么时候需要回溯?什么时候不需要回溯?_递归

什么时候不需要回溯:

当遇到一个选择时,一定要对它操作时,那么就不需要回溯。比如要标记求所有情况,找到了就要标记。如果取消标记,那么就会重复计算。

例:求连通块的个数

1表示陆地,0表示海洋

dfs(x,y):
  if(x,y越界)return;
  if(g[x][y]==0)return;
  g[x][y]=0;
  dfs(x+1,y);
  dfs(x-1,y);
  dfs(x,y+1);
  dfs(x,y-1);

求马的覆盖点:

马的覆盖点_YRZ-James的博客

标记马三步内可能走过的所有点,搜索到一种情况就要标记,所以不用回溯

void dfs(int x,int y,int step)
{
	//一个位置可以重复的走 
	if(!(x>=1&&y>=1&&x<=n&&y<=m))return;
	if(step>3)return;
	G[x][y]='#';
	dfs(x+2,y-1,step+1);
	dfs(x+2,y+1,step+1);
	dfs(x-2,y-1,step+1);
	dfs(x-2,y+1,step+1);
	dfs(x+1,y-2,step+1);
	dfs(x+1,y+2,step+1);
	dfs(x-1,y-2,step+1);
	dfs(x-1,y+2,step+1);
}

注:一个位置可以多次访问,所以不用vis标记去重

例:机器人的运动范围

机器人的运动范围_牛客题霸_牛客网

int res=0;    
void dfs(int x,int y,int threshold,int rows,int cols){
        if(!(x>=0&&x<rows&&y>=0&&y<=cols))return;
        if(cnt[x][y]>threshold)return;
        if(vis[x][y])return;
        vis[x][y]=true;
        res++;
        dfs(x+1,y,threshold,rows,cols);
        dfs(x,y+1,threshold,rows,cols);
    }