走迷宫
0302:走迷宫
一、题目
总时间限制:
1000ms
内存限制:
65536kB
描述
一个迷宫由R行C列格子组成,有的格子里有障碍物,不能走;有的格子是空地,可以走。
给定一个迷宫,求从左上角走到右下角最少需要走多少步(数据保证一定能走到)。只能在水平方向或垂直方向走,不能斜着走。
输入
第一行是两个整数,R和C,代表迷宫的长和宽。( 1<= R,C <= 40)
接下来是R行,每行C个字符,代表整个迷宫。
空地格子用'.'表示,有障碍物的格子用'#'表示。
迷宫左上角和右下角都是'.'。
输出
输出从左上角走到右下角至少要经过多少步(即至少要经过多少个空地格子)。计算步数要包括起点和终点。
样例输入
5 5
..###
#....
#.#.#
#.#.#
#.#..
样例输出
9
二、问题分析
想要看是否能走出迷宫,必然遍历每一个点,看是否能到终点,可分为两种遍历方式。
1. 深度优先遍历
沿着树的深度遍历树的节点,尽可能深地搜索树的分支。当节点的所在边都己被探寻过,搜索将回溯到发现节点的那条边的起始节点。
这一过程一直进行到已发现从源节点可达的所有节点为止。
2. 广度优先遍历
从根节点(或起始节点)开始,逐层地对节点进行访问,即先访问距离起始节点最近的所有节点,然后再依次访问距离更远一层的节点,以此类推,直到遍历完所有可达节点
三、设计解决方案
1)队列(BFS)
- 输入处理:读取迷宫的行数和列数,并读取迷宫的二维字符数组。
- 初始化:创建一个队列,用于存储待访问的节点。将起点(左上角)加入队列,并标记为已访问。
- BFS 搜索:
- 从队列中取出一个节点。
- 检查该节点是否为终点(右下角),如果是,则返回当前步数。
- 扩展该节点的相邻节点(上、下、左、右),如果相邻节点是空地且未被访问过,则将其加入队列,并标记为已访问。
- 步数加 1。
- 输出结果:如果找到终点,则输出最少步数;否则,输出无解。
2)栈(DFS)
1.输入处理:
- 读取输入的迷宫行数和列数,并将迷宫布局存储在二维字符数组中,创建一个同样大小的二维数组 `vis`
- 定义两个数组分别代表水平和垂直方向的偏移量,用于表示右、下、左、上四个移动方向。
2.深度优先搜索函数实现
- 编写 `dfs` 函数,接收当前格子坐标和已走步数作为参数。
- 在函数内,先判断当前格子是否为终点(右下角)。
- 遍历四个方向,对于每个方向计算新坐标。
- 检查新坐标是否越界、是否为障碍物或已被访问,若不满足条件则跳过。
- 若新坐标合法,将其标记为已访问,递归调用 dfs 函数继续搜索,步数加 1。
- 递归返回后,将新坐标标记为未访问,以便后续尝试其他路径。
- 启动搜索
将起点标记为已访问,调用 dfs 函数,初始步数设为 1。 - 结果输出
搜索结束后,结果为false,说明无法到达终点,输出 -1;否则输出s,即最短路径步数。
四、代码展示
1)队列(BFS)
#include <bits/stdc++.h>
using namespace std;
const int N=55;
#define x first
#define y second
int n,m;
int d[N][N];
char mp[N][N];
int dx[4]={0,1,0,-1};
int dy[4]={1,0,-1,0};//四个方向(北、东、南、西)
void bfs()
{
memset(d,0x3f,sizeof d);//初始时所有位置到起点的距离为无穷大
queue<pair<int,int>>q;
d[1][1]=1;//起点
q.push({1,1});
while(q.size())
{
int x=q.front().x;//取横坐标
int y=q.front().y;//取纵坐标
q.pop();//走过的弹出
for(int i=0;i<4;i++)
{
int x1=x+dx[i];
int y1=y+dy[i];//走
if(x1<1||x1>n||y1<1||y1>m){
continue;}//越界
if(mp[x1][y1]=='#'){
continue;}//不能走
if(d[x1][y1]>d[x][y]+1){
d[x1][y1]=d[x][y]+1;
q.push({x1,y1});
}//*重要!*替换成更短的路径
}
}
}
int main(){
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);//用于解除cin、cout与标准C输入输出流的同步,提高输入输出的效率
cin>>n>>m;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
cin>>mp[i][j];//输入地图
}
}
bfs();
if(d[n][m]==0x3f3f3f3f){
cout<<-1;
}//*如果仍为初始值则没有到达终点
else{
cout<<d[n][m];
}
return 0;
}
- 时间复杂度:O(n∗m),n 和 m 分别是迷宫的行数和列数。在最坏情况下,需要遍历迷宫中的每个格子。
- 空间复杂度:O(n∗m),主要用于存储队列和 数组 d。
2)栈(DFS)
#include <bits/stdc++.h>
using namespace std;
const int N=20;
int vis[N][N];
char a[N][N];
int n,m;
int dx[4]={0,1,0,-1};
int dy[4]={1,0,-1,0};//四个方向(北、东、南、西)
bool dfs(int x,int y,int s)//s指步数
{
if(x==n&&y==m){
cout<<s;
return true;}//已走到终点
for(int i=0;i<4;i++)
{
int xx=x+dx[i];int yy=y+dy[i];//走四个方向
if(vis[xx][yy]){
continue;
}//已走过,不走
if(a[xx][yy]=='#'){
continue;}//走到‘#’,不走
if(xx<1||yy<1||xx>n||yy>m){
continue;}//走过头(出界)
vis[xx][yy]=1;//标记为走过了
if (dfs(xx,yy,s+1)){
return true;//*重要*如果递归调用找到终点了,直接返回true
}
vis[xx][yy]=0; //*重要*回溯操作,将下一个位置标记为未访问,以便下个循环尝试其他路径。
}
return false;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++){
cin>>a[i][j];//输入地图
vis[i][j]=0;//初始化vis=0}
}
vis[1][1]=1;//第一个点为起点
if(!dfs(1,1,1)){
cout << -1;}
return 0;
}
- 时间复杂度:O(n∗m),在最坏情况下,DFS 可能需要遍历所有可能的路径.
- 空间复杂度:O(n∗m),主要用于递归调用栈和 vis 数组
五、对比总结
- 这是一个典型的广度优先搜索(BFS)(队列)问题。bfs是一种用于遍历或搜索树或图的算法,它从根节点(本题中为迷宫的左上角)开始,逐层地访问节点,直到找到目标节点(本题中为迷宫的右下角)。由于 BFS 是按层遍历的,所以当第一次到达目标节点时,所经过的路径一定是最短的。
- BFS:适合求解最短路径问题,因为它按层遍历,能保证第一次到达终点时的路径就是最短路径。但空间复杂度相对较高,需要使用队列存储待访问的节点。
- DFS:更适合求解所有可能路径的问题,但在求解最短路径问题时效率较低,因为需要遍历所有可能的路径并记录最短路径。空间复杂度主要取决于递归调用栈的深度。
- 所以,在迷宫问题中,通常优先选择 BFS 来求解最短路径。