走迷宫

0302:走迷宫

一、题目

总时间限制:

1000ms

内存限制:

65536kB

描述

一个迷宫由R行C列格子组成,有的格子里有障碍物,不能走;有的格子是空地,可以走。
给定一个迷宫,求从左上角走到右下角最少需要走多少步(数据保证一定能走到)。只能在水平方向或垂直方向走,不能斜着走。

输入

第一行是两个整数,R和C,代表迷宫的长和宽。( 1<= R,C <= 40)
接下来是R行,每行C个字符,代表整个迷宫。
空地格子用'.'表示,有障碍物的格子用'#'表示。
迷宫左上角和右下角都是'.'。

输出

输出从左上角走到右下角至少要经过多少步(即至少要经过多少个空地格子)。计算步数要包括起点和终点。

样例输入

5 5
..###
#....
#.#.#
#.#.#
#.#..

样例输出

9

二、问题分析

  想要看是否能走出迷宫,必然遍历每一个点,看是否能到终点,可分为两种遍历方式。

1. 深度优先遍历

沿着树的深度遍历树的节点,尽可能深地搜索树的分支。当节点的所在边都己被探寻过,搜索将回溯到发现节点的那条边的起始节点。

这一过程一直进行到已发现从源节点可达的所有节点为止。

2. 广度优先遍历

从根节点(或起始节点)开始,逐层地对节点进行访问,即先访问距离起始节点最近的所有节点,然后再依次访问距离更远一层的节点,以此类推,直到遍历完所有可达节点

三、设计解决方案

1)队列(BFS)

  1. 输入处理:读取迷宫的行数和列数,并读取迷宫的二维字符数组。
  2. 初始化:创建一个队列,用于存储待访问的节点。将起点(左上角)加入队列,并标记为已访问。
  3. BFS 搜索
    • 从队列中取出一个节点。
    • 检查该节点是否为终点(右下角),如果是,则返回当前步数。
    • 扩展该节点的相邻节点(上、下、左、右),如果相邻节点是空地且未被访问过,则将其加入队列,并标记为已访问。
    • 步数加 1。
  4. 输出结果:如果找到终点,则输出最少步数;否则,输出无解。

2)栈(DFS)

1.输入处理:

- 读取输入的迷宫行数和列数,并将迷宫布局存储在二维字符数组中,创建一个同样大小的二维数组 `vis`
- 定义两个数组分别代表水平和垂直方向的偏移量,用于表示右、下、左、上四个移动方向。

2.深度优先搜索函数实现

- 编写 `dfs` 函数,接收当前格子坐标和已走步数作为参数。
- 在函数内,先判断当前格子是否为终点(右下角)。
- 遍历四个方向,对于每个方向计算新坐标。
- 检查新坐标是否越界、是否为障碍物或已被访问,若不满足条件则跳过。
- 若新坐标合法,将其标记为已访问,递归调用 dfs 函数继续搜索,步数加 1。
- 递归返回后,将新坐标标记为未访问,以便后续尝试其他路径。
  1. 启动搜索
    将起点标记为已访问,调用 dfs 函数,初始步数设为 1。
  2. 结果输出
    搜索结束后,结果为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 来求解最短路径。

注:本文由两人共同完成,林宇钦陈心琳

posted @ 2025-03-24 21:23  Angelguaiguai  阅读(10)  评论(0)    收藏  举报
levels of contents