什么时候需要回溯:
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);
如迷宫问题
什么时候不需要回溯:
当遇到一个选择时,一定要对它操作时,那么就不需要回溯。比如要标记求所有情况,找到了就要标记。如果取消标记,那么就会重复计算。
例:求连通块的个数
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);
}