【小白学算法】bfs超详细解析+例题[kuangbin]简单搜索-三维地宫

题面

你被困在一个3D地牢中,需要找到最快的逃离方式!地牢由单位立方体组成,这些立方体可能填满了岩石,也可能没有填满。向北、南、东、西、上或下移动一个单位需要一分钟的时间。你不能对角线移动,迷宫的四周被坚实的岩石包围着。

是否可能逃脱?如果是,需要多长时间?

输入

输入包括多个地牢。每个地牢描述以包含三个整数 L、R 和 C(均限制为 30 大小)的一行开始。
L 是构成地牢的层数。
R 和 C 是构成每个层面平面的行数和列数。
然后将跟随 L 个块,每个块包含 R 行,每行包含 C 个字符。每个字符描述地牢的一个单元格。一个充满岩石的单元格用 '#' 表示,空单元格用 '.' 表示。你的起始位置由 'S' 表示,出口由字母 'E' 表示。每个层面后都有一个空行。输入以 L、R 和 C 三个零终止。

输出

每个迷宫生成一行输出。如果可以到达出口,打印形式如下的一行:

Escaped in x minute(s).

其中 x 被替换为逃脱所需的最短时间。
如果无法逃脱,打印以下行:

Trapped!

样例

Input Output
3 4 5
S....
.###.
.##..
###.#

#####
#####
##.##
##...

#####
#####
#.###
####E

1 3 3
S##
#E#
###
0 0 0
Escaped in 11 minute(s).
Trapped!

题目分析

这是一道经典的三维bfs最短路径问题,由题面可知 你被困在一个3D立方体地牢中(L为层数,每层有R行C列)每个位置是一个单位立方体:

  •  `'#'` 表示岩石,不能走
    
  •  `'.'` 表示空地,可以走
    
  •  `'S'` 表示起点
    
  •  `'E'` 表示出口
    

    移动规则:

  •  只能向,前、后、左、右、上、下六个方向走
    
  •  一步为一分钟
    
  •  不能斜着走,即只能走六个方位的正方向
    

    要求:

  • 如果能找到出口,按要求输出最短时间
    
  • 不能则输出“Trapped!”
    

解题思路

首先,在输入地图时找到入口位置S(即你的bfs的起点)以及出口位置E(你的bfs的终点),并记录坐标,由于我们需要找到出口,其实本质就是找到最短路径(dfs也可以实现,但是dfs“一条路走到黑”且不一定找到出口的情况在时间复杂度上效率太低,而bfs只需将所有格子访问一次就可以判断能否逃脱并的出最短路径),所以根据广度优先原则,首先入口入队列,作为起点,其次遍历每个位置的六个方向,判断是否可走,可走的方位坐标入队列,然后一次取出队顶的坐标继续判断,直至找到出口位置,由于我们是广度优先,所以只要能到达出口位置,此时的路径一定是最短路径,当所有位置都访问完后依然没有找到出口,则说明本图没有路径可从入口到达出口,即被困。

本题的“坑”

  1. L,R,C不超过30,可以用char str[31][31][31]来存图,每个单元格单独读取即可,(不能用string直接读整行,应为输入时没层之间有空行)

  2. 防止重复走,所以我们每次入队列一个坐标后就把该点标记为"#",表示不可走。

  3. 在判断是否可走的时候不能只判断“ . ”,还要判断"E",即只要不为"#"就可以走。

完整题解

#include<iostream>
#include<cstring>
#include<iomanip>
#include<queue>
using namespace std;
int n;
int l,r,c;
int m, arr[11];  // arr 用于记录方案
bool vis[10][10];
//int ans=0;
int orx,ory,orz;
int ulx,uly,ulz;
char str[31][31][31];

struct root{
    int x;
    int y;
    int z;
    int depth;
}poi;
int dx[6]={-1,1,0,0,0,0};
int dy[6]={0,0,-1,1,0,0};
int dz[6]={0,0,0,0,-1,1};//六个方向坐标的变化
int bfs(){
    queue<root> q;
    root point;
    point.x=orx;
    point.y=ory;
    point.z=orz;
    point.depth=0;
    q.push(point);//入口点入队列,作为bfs的起点
    while(!q.empty()){//队列为空只有一种情况,所有点全判断完
        root p,t=q.front();
        q.pop();
        if(t.x==ulx&&t.y==uly&&t.z==ulz){
            return t.depth;
        }
        for(int i=0;i<6;i++){
            int nx=t.x+dx[i],ny=t.y+dy[i],nz=t.z+dz[i];//每个点判断六个方向的单元格
            if(nx>=0&&nx<l&&ny>=0&&ny<r&&nz>=0&&nz<c&&str[nx][ny][nz]!='#'){//边界检查,保证坐标是合法的
                str[nx][ny][nz]='#';//标记访问过
                p.depth=t.depth+1;//访问该位置,步数(即bfs深度)加1
                p.x=nx;
                p.y=ny;
                p.z=nz;
                q.push(p);//可行位置入队
            }
        }
    }//所有点全判断完后依然没有路径可达,则返回0,说明不可达
    return 0;

}
int main(){
    while(cin>>l>>r>>c&&l!=0&&r!=0&&c!=0){
//        ans=0;
        for(int i=0;i<l;i++){
           for(int j=0;j<r;j++){
               for(int k=0;k<c;k++)
               {
                   cin>>str[i][j][k];
                   if(str[i][j][k]=='S') {
                       orx = i, ory = j, orz = k;//记录入口坐标
                   }else if(str[i][j][k]=='E'){
                       ulx=i,uly=j,ulz=k;//记录出口坐标
                   }
               }
           }
        }
        int ans=bfs();//记录图的“最短路径”
        if(ans){//若有值则可以逃脱,否则无法逃脱
            cout<<"Escaped in "<<ans<<" minute(s)."<<endl;
        }else{
            cout<<"Trapped!"<<endl;
        }
    }

    return 0;
}
posted @ 2025-08-08 13:32  芝士青瓜不拿铁  阅读(16)  评论(0)    收藏  举报