迷宫问题
定义一个二维数组:
int maze[5][5] = {
0, 1, 0, 0, 0,
0, 1, 0, 1, 0,
0, 0, 0, 0, 0,
0, 1, 1, 1, 0,
0, 0, 0, 1, 0,
};
它表示一个迷宫,其中的1表示墙壁,0表示可以走的路,只能横着走或竖着走,不能斜着走,要求编程序找出从左上角到右下角的最短路线。
Input
一个5 × 5的二维数组,表示一个迷宫。数据保证有唯一解。
Output
左上角到右下角的最短路径,格式如样例所示。
Sample Input
0 1 0 0 0
0 1 0 1 0
0 0 0 0 0
0 1 1 1 0
0 0 0 1 0
Sample Output
(0, 0)
(1, 0)
(2, 0)
(2, 1)
(2, 2)
(2, 3)
(2, 4)
(3, 4)
(4, 4)
思路分析:
这是一道很经典的遍历图找最短路问题,遍历图有两种方法:深度优先遍历(dfs)和广度优先遍历(bfs),由于是要寻找最短路,采用bfs效率更高。不过此题的图并不大,用两种方法均不会超时。但此题有一个难点:输出最短路径。这就需要在原来的dfs和bfs模板上加一些存储路径的方法。
bfs解法:
将一个格子看成一个结点,该结点包含三个成员:int x,y,pre。
x和y表示该结点的位置,pre用于指向父结点(即将该结点扩展出来的结点)。当扩展到目标结点(4,4)时,可以通过pre获取到前一结点,最终回到初始结点。由于路径需要正向输出,而我们是从最终结点反推回初始结点,因此需要利用栈(递归)来输出路径。具体代码如下:
#include <cstdio>
#include <algorithm>
using namespace std;
int a[5][5];
typedef struct
{
int x;
int y;
int pre; //存放父结点的编号
}node;
typedef struct
{
node data[1000]; //存放结点的队列
int front; //队列头部
int rear; //队列尾部
}Queue;
void print(Queue qu, int cur) //获取该队列和当前结点的参数,由于要倒序输出,因此用递归(调用栈)
{
if(qu.data[cur].pre == -1)
{
printf("(%d, %d)\n",qu.data[cur].x, qu.data[cur].y);
return;
}
print(qu, qu.data[cur].pre);
printf("(%d, %d)\n",qu.data[cur].x, qu.data[cur].y);
}
void path(int xi, int yi, int xe, int ye) //从(xi,yi)走到(xe,ye)
{
int ok = 0;
Queue qu; //创建队列
qu.rear = 0; //初始化队列
qu.front = -1;
qu.data[0].x = 0;
qu.data[0].y = 0;
qu.data[0].pre = -1; //初始结点的pre设为-1,作为输出路径的递归出口
a[xi][yi] = 2; //表示该结点已经遍历过
while(qu.front != qu.rear && ok == 0) //若队列不为空 (队列为空表示所有能扩展的结点都扩展完了也没能发现目标结点,表示无解)
{
qu.front++; //出队,front指向出队结点
if(qu.data[qu.front].x == xe && qu.data[qu.front].y == ye) //若出队的结点是目标结点
{
ok = 1;
//printf("迷宫有解。\n");
print(qu, qu.front); //根据该结点反推到初始结点并输出
}
else
{
int k;
int i = qu.data[qu.front].x, j = qu.data[qu.front].y;
for(k = 0;k < 4;k++) //循环4次,扩展该出队结点
{
i = qu.data[qu.front].x, j = qu.data[qu.front].y;
switch(k)
{
case 0: i--; break;
case 1: i++; break;
case 2: j--; break;
case 3: j++; break;
}
if(a[i][j] == 0 && i >= 0 && i <= 4 && j >= 0 && j <= 4) //扩展结点可用
{
qu.rear++; //将扩展的结点进队并初始化进队结点的坐标和并将pre设为父结点的编号(之后可通过pre找到父结点)
qu.data[qu.rear].x = i, qu.data[qu.rear].y = j;
qu.data[qu.rear].pre = qu.front;
a[i][j] == 2; //表示该结点已经遍历过,避免后面重复遍历
}
}
}
}
if(ok == 0) printf("无解。\n");
}
int main()
{
int i, j; //初始化迷宫
for(i = 0;i < 5;i++)
for(j = 0;j < 5;j++)
scanf("%d",&a[i][j]);
path(0,0,4,4);
return 0;
}
dfs解法:
dfs同样需要将一个格子建成一个结点,该结点包含x,y成员,并建立两个结点数组:path[25], way[25],path用于记录当前所搜的路径,way用于最短路径。因此每搜到一个可行的结点就将该结点存进path数组,由于dfs是按顺序一个一个格子扩展,因此路径可以直接按顺序输出结点数组。由于需要求最短路径,则用一个变量记录步数,若当前遍历的路径比之前搜过的路径步数还要少,就将path数组更新给way数组,最后再将way数组输出即可。具体代码如下:
#include <cstdio>
#include <algorithm>
using namespace std;
int a[5][5], ans = 99999;
typedef struct //若要记录路径,则应该运用结点数组,每遍历一次,就往结点存当前坐标值,最后按顺序将结点输出即可得到路径
{
int x;
int y;
}node;
node path[25], way[25]; //path用于存当前路径,way用于存最短路径
void dfs(node cur, int count) //状态为当前结点,当前步数
{
int i, j, k;
if(ans > count && cur.x == 4 && cur.y == 4) //寻到短路径
{
ans = count;
for(i = 0;i < count;i++) //存路径给way
{
way[i].x = path[i].x;
way[i].y = path[i].y;
}
}
else
{
for(k = 0;k < 4;k++) //遍历4个方向
{
i = cur.x, j = cur.y;
switch(k)
{
case 0: i++; break;
case 1: i--; break;
case 2: j++; break;
case 3: j--; break;
}
if(i >= 0 && j >= 0 && i <= 4 && j <= 4 && a[i][j] == 0) //不超范围,不重复遍历,则扩展的结点可走
{
path[count].x = i; //给扩展结点存当前坐标值
path[count].y = j;
a[i][j] = 2; //标示已走路径
dfs(path[count], count+1); //移步到该扩展结点的状态
a[i][j] = 0; //回溯
}
}
}
}
int main()
{
int i, j;
for(i = 0;i < 5;i++)
for(j = 0;j < 5;j++)
scanf("%d",&a[i][j]);
path[0].x = 0, path[0].y = 0;
dfs(path[0], 1);
for(i = 0;i < ans;i++)
printf("(%d, %d)\n",way[i].x, way[i].y);
return 0;
}
有其他疑问或建议可在评论区留言。