说到最短路径,大部分最会想到BFS,DFS求最短路径还是比较少的,我觉得比如只有一种路径可走的话,可以用DFS。
但是这题比较特殊,因为它的规则不像普通迷宫那么简单。具体就不说了。
DFS求最短路径? 那只能是把所有可能到目的地的方法所需步数都比较一下,保存最小的那个值。
所以,搜到一个解之后不能退出DFS,要继续把余下的压在栈里的递归层跑完(当然,跑的时候又会产生新的递归层压入栈),直到所有的情况都遍历了一遍,才能得出最优解。
所以 DFS 函数应该是void型的。
void dfs(int x,int y,int step)
{
……
if(……){
map[nx][ny]=0;
dfs(ex,ey,step+1);
map[nx][ny]=1;
}
……
}
如果找到一个解就退出是这样的:
int dfs(---){
while(---){
if(找到解) return 1;
……
if(dfs()) return 1;
……
}
return 0;
}
由于有step>10就return 以及 越界就不dfs。所以肯定不存在会死循环的问题。
总结:
对于dfs,不要盲目的套形式,dfs只是一个对于某些数据范围小的问题比较有效的工具,它往往和回溯的思想结合再一起,同事具备遍历全图的能力。拿到题目,如果你意识到这可能是一道dfs题,你首先要明白你需要dfs来完成什么工作,从而确定dfs的形式。
对于递归,就是入栈和出栈的过程,i+1层运行完之后,退回到第i当时进入第i+1层的位置,且此时某些变量还是保持第i层的值。直到第一层运行完(即栈空),函数运行结束。
顺便BB一下:我们知道DFS遍历全图的复杂度基本是N*N,还要递归,找出所有可能的方法,DFS的次数肯定比要遍历全图还高的多。而且N达到了20。所以注意到一点,题目规定了,步数大于10可以直接算失败。想想如果没有这个规定不知道怎么做出来。
详细注释代码:
#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
int map[22][22],m,n,sx,sy;
int dir[][2]={1,0,-1,0,0,1,0,-1};
int ans;
void dfs(int x,int y,int step)
{
int nx,ny,ex,ey; //(nx,ny) 代表下一步 (ex,ey) 代表走的这一步
if(step>10) return ; //实际上是剪枝
for(int i=0;i<4;i++)
{
nx=x+dir[i][0]; //下一步
ny=y+dir[i][1];
ex=x;
ey=y;
while(nx>=0 && nx<m && ny>=0 && ny<n && map[nx][ny]!=1) //为真代表下一步可走
{
ex+=dir[i][0]; //则走这一步
ey+=dir[i][1];
if(map[ex][ey]==3) //走的这步是终点吗。
{
ans=(step<ans?step:ans);printf("%d\n",step);
return;
}
nx=ex+dir[i][0]; //走完这一步的下一步
ny=ey+dir[i][1];
if(nx<0 || nx>=m || ny<0 || ny>=n)
break; //如果下一步是越界,说明会直接滑出去,不必再进行dfs
if(map[nx][ny]==1) //如果下一步是1,进行深搜.
{
map[nx][ny]=0;
dfs(ex,ey,step+1);
map[nx][ny]=1;
}
//如果下一步还是0,按照题意,当然要继续滑下去,继续while
}
}
}
int main()
{
while(~scanf("%d%d",&n,&m),m||n)
{
for(int i=0;i<m;i++)
for(int j=0;j<n;j++)
{
scanf("%d",&map[i][j]);
if(map[i][j]==2) {sx=i;sy=j;}
}
ans=100000;
dfs(sx,sy,1);
if(ans==100000)
printf("-1\n");
else
printf("%d\n",ans);
}
return 0;
}