问题描述
下图给出了一个迷宫的平面图,其中标记为 1 的为障碍,标记为 0 的为可以通行的地方。
010000
000100
001001
110000
迷宫的入口为左上角,出口为右下角,在迷宫中,只能从一个位置走到这 个它的上、下、左、右四个方向之一。对于上面的迷宫,从入口开始,可以按DRRURRDDDR 的顺序通过迷宫, 一共 10 步。其中 D、U、L、R 分别表示向下、向上、向左、向右走。
对于下面这个更复杂的迷宫(30 行 50 列),请找出一种通过迷宫的方式, 其使用的步数最少,在步数最少的前提下,请找出字典序最小的一个作为答案。 请注意在字典序中D<L<R<U。
(题源:蓝桥杯2019省赛)
题目分析
迷宫问题有两种问法:
1.求所有能够从起点到终点的路径 采用dfs+回溯法
2.求最短路径 采用bfs
dfs与bfs的不同点:
1.dfs是一种“一往无前,不撞南墙不回头”的搜索方法,因此它每次搜索成功的路径的长度是不确定的,即不一定是最短路径。
2.而bfs是一种“道生一,一生二,二生三,三生万物”似的搜索方式,就像火影鸣人的分身一样,一圈一圈地向外扩展,
因此它寻找到的路径一定是最短路径。
具体bfs实现思路为
用数据结构队列来存储每一个点,因为要求在最短的前提下字典序还要最小,所以要对探索的顺序进行一个排序
即 上 左 右 下 (D,L,R,U),用一个数组pre来存储每一个点是通过哪种操作到达的,同时还要有一个visit数组来
记录某点是否已经访问过了。
参考code
#include<bits/stdc++.h>
using namespace std;
int maze[50][50],visit[50][50] , pre[50][50];
struct node{
int x,y; //结构体,表示节点的横纵坐标
};
int dx[4] = {1,0,0,-1},dy[4] = {0,-1,1,0}; //代表四个方向
int n,m;//代表行与列
char ch[4] = {'D','L','R','U'}; //D 向下,U向上,L向左,R向右
queue<node>qu; //队列
void bfs( )
{
node start;
start.x = 1;start.y=1; //默认迷宫的出发点为(1,1)
qu.push(start); //入队列
visit[1][1] = 1; //第一个点已经访问过了
while(!qu.empty()) // 如果队列不空
{
node now = qu.front(); //队头元素,并出队
qu.pop();
for(int i=0;i<4;i++) //遍历四个方向,注意按顺序来
{
int xx = now.x+dx[i],yy = now.y+dy[i]; //记录下一个节点
node next;
next.x = xx;
next.y = yy; //注意边界条件,迷宫墙壁,是否已经访问过
if(xx<1||xx>n||yy<1||yy>m||maze[xx][yy]||visit[xx][yy]) continue;
pre[xx][yy] = i; //记录该节点的前驱,即通过哪种方式到达该点的
visit[xx][yy] = 1; // 记录已经遍历过了
qu.push(next); // 入队列
}
}
}
int main()
{
n = 30;
m = 50;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
char a;
cin>>a;
maze[i][j]=a-'0';
}
bfs();
char ans[200];
int num = 0;
while(n!=1 || m!=1)
{ // 倒序存储下来,从终点开始计算
int i = pre[n][m];
ans[num++] = ch[i];
n -=dx[i];
m -=dy[i];
}
for(int i=num-1;i>=0;i--)
cout<<ans[i];
}
最后结果为DDDDRRURRRRRRDRRRRDDDLDDRDDDDDDDDDDDDRDDRRRURRUURRDDDDRDRRRRRRDRRURRDDDRRRRUURUUUUUUULULLUUUURRRRUULLLUUUULLUUULUURRURRURURRRDDRRRRRDDRRDDLLLDDRRDDRDDLDDDLLDDLLLDLDDDLDDRRRRRRRRRDDDDDDRR