问题引入
有一天,小玄一个人去玩迷宫,但是方向感很不好的他迷路了。小澈知道后便去解救无助的小玄。小澈是有备而来,已经弄清楚了迷宫的地图,现在小澈要以最快的速度去解救小玄。问题开始了......
迷宫由n行m列的单元格组成(n,m < 50),每个单元格要么是空地,要么是障碍物。
你的任务是帮小澈找到一条从迷宫的起点到小玄所在位置的最短路径。
注意障碍物是不能走的,也不能走到迷宫外哦。
问题解析
首先我们可以用一个二维数组来储存这个迷宫。刚开始时小澈位于迷宫的入口处(1,1),小玄在(p,q),最开始只能向右或者是向下走。现在只有一个小澈,我们只能一个一个去尝试。我们可以先让小澈往右走,直到走不通时再回到这里。这里我们规定一个顺序,按照顺时针的方向来尝试(即按照右,下,左,上的顺序去尝试)。
第一次尝试后的结果如下:
我们这样只是尝试了一条路,并不一定是最短的。在刚才的很多地方我们有很多选择方向都是可以有多重选择的,所以我们需要把所以的选择都尝试一遍,最后输出最短的一条路径。
代码实现
现在我们尝试用深度优先搜索的方式来解决这个问题。来看看dfs这个函数怎么写。
dfs函数
dfs函数的功能是用来解决当前应该怎么办。而小澈在某个点的时候需要处理的是:先检查小澈是否到达了小玄的位置,如果没有到达则找出下一步可以走的地方。
为了解决这个问题,dfs函数需要维护三个参数,分别是
- 当前这个点的x坐标
- 当前这个点的y坐标
- 当前已经走过的步数step
voiddfs(intx, inty, intstep) { return; }
判断是否已经到达小玄的位置这一点很好实现,只需要判断当前的坐标是否和小玄的位置相等即可
voiddfs(intx, inty, intstep) { //判断是否到达小玄的位置 if (x == p && y == q) { //更新最小值 if (step < min) min = step; return; //请注意这里的返回很重要 } return; }
如果我们没有到达小玄的位置,则找出下一步可以走的地方。因为有四个方向可以走,根据我们之前的约定,按照顺时针的方向来尝试(即按照右,下,左,上的顺序去尝试)。为了编程方便,我们定义了一个方向数组next,如下:
intnext[4][2] = {{0,1}, //向右走 {1,0}, //向下走 {0,-1 }, //向左走 {-1,0} //向上走 };
通过这个方向数组,使用循环我们就能很容易的获得下一步的坐标。这里将下一步的横坐标用tx存储,纵坐标用ty来存储。
for (k = 0; k <= 3; k++) { //计算下一个点的坐标 tx = x + next[k][0]; ty = y + next[k][1]; }
接下来我们要对下一个点的(tx,ty)进行一些判断。包括是否越界,是否为障碍物,以及这个点是否在路径中(避免重复访问一个点)。需要用一个book[tx][ty]来记录格子(tx,ty)是否已经在路径中。
如果这个点符合要求,就对这个点进行下一步的扩展,即dfs(tx,ty,step+1),注意这里是step+1,因为你一旦从这个点开始往下继续尝试,就意味着你的步数已经增加了1.
代码实现如下:
for (k = 0; k <= 3; k++) { //计算下一个点的坐标 tx = x + next[k][0]; ty = y + next[k][1]; //判断是否越界 if (tx < 1 || tx > n || ty < 1 || ty > m) continue; //判断该点是否为障碍物或者已经在路径中 if (a[tx][ty] == 0 && book[tx][ty] == 0) { book[tx][ty] = 1; //标记这个点已经走过 dfs(tx, ty, step + 1); //开始尝试下一个点 book[tx][ty] = 0; //尝试结束,取消这个点的标记 } }
完整代码
#include <stdio.h> intn, m, p, q, min = 99999999; inta[51][51], book[51][51]; voiddfs(intx, inty, intstep) { intnext[4][2] = { {0,1}, //向右走 {1,0}, //向下走 {0,-1 }, //向左走 {-1,0} //向上走 }; inttx, ty, k; //判断是否到达小玄的位置 if (x == p && y == q) { //更新最小值 if (step < min) min = step; return; //请注意这里的返回很重要 } //枚举四种走法 for (k = 0; k <= 3; k++) { //计算下一个点的坐标 tx = x + next[k][0]; ty = y + next[k][1]; //判断是否越界 if (tx < 1 || tx > n || ty < 1 || ty > m) continue; //判断该点是否为障碍物或者已经在路径中 if (a[tx][ty] == 0 && book[tx][ty] == 0) { book[tx][ty] = 1; //标记这个点已经走过 dfs(tx, ty, step + 1); //开始尝试下一个点 book[tx][ty] = 0; //尝试结束,取消这个点的标记 } } return; } intmain() { inti, j, startx, starty; //读入n和m,n为行,m为列 scanf("%d %d", &n, &m); //读入迷宫 for (i = 1; i <= n; i++) { for (j = 1; j <= m; j++) scanf("%d", &a[i][j]); } //读入起点和终点的坐标 scanf("%d %d %d %d", &startx, &starty, &p, &q); //从起点开始搜索 book[startx][starty] = 1; //标记起点已经在路径中,防止后面重复走 //第一个参数是起点的x坐标,第二个参数是起点的y坐标,第三个参数是初始步数为0dfs(startx, starty, 0); //输出最短步数 printf("%d", min); getchar(); getchar(); return0; }
总结
发明深度优先算法的John E.Hopcroft 和 Robert E .Tarjan.1971~1972年,他们在斯坦福大学研究图的连通性(任意两点是否可以互相到达)和平面性(图中所有的边不交叉。在电路板上设计布线的时候,要求线和线不能交叉。这就是平面性的一种应用)
今天的内容就分享到这啦!!
如果觉得有帮助,请: