题目描述

你被困在一个 L×R×CL\times R\times C(均 ≤30\le 30)的三维立方体空间里,每格为 #(不可通过)、.(可通行)、S(起点,唯一)或 E(出口,唯一)。每次只能朝上下前后左右六个方向之一移动到相邻格,耗时 1 分钟;不可对角线移动。求 S 到 E 的最少分钟数,不可达输出 Trapped!。多组数据,每组先给 L R C,再依次给 LL 层、每层 RR 行 CC 列的网格(层间有空行);0 0 0 结束。样例 1:3 4 5 + S..../.###./.##../###.# + ####/####/##.##/##... + ####/####/#.###/####E → Escaped in 11 minute(s).;样例 2:1 3 3 + S##/#E#/### → Trapped!。

思路

把三维网格建成无权图:可通行格是顶点,六方向相邻就连一条无向边,S 到 E 的最短距离就是答案。边权全为 1,所以从 S 出发 BFS 的层号就是"已用分钟数",命中 E 的层号直接返回;入队即标记保证每格只展开一次,复杂度 O(LRC)O(LRC)。

参考代码

Python 版:

python
#!/usr/bin/env python3
# Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
# rbook: -> https://rbook.roj.ac.cn  https://rbook2.roj.ac.cn
# rainboy的学习导航网站: https://idx.roj.ac.cn
# create_at: 2026-09-30 01:36
# update_at: 2026-09-30 01:36

import sys

WALL = ord('#')  # 网格里存的是字节值,'#' 的字节值是 35,不可通行单元的判据
START, EXIT = ord('S'), ord('E')

# 六个方向的 (层, 行, 列) 偏移:层 ±1 是上下,行 ±1 是前后,列 ±1 是左右
DIRS: tuple[tuple[int, int, int], ...] = (
    (1, 0, 0), (-1, 0, 0), (0, 1, 0), (0, -1, 0), (0, 0, 1), (0, 0, -1)
)


def find(dungeon: list[list[bytes]], key: int) -> tuple[int, int, int]:
    """定位标记字符 key(S 或 E)所在的 (层, 行, 列);题面保证它出现且只出现一次。"""
    return next(
        (l, r, c)
        for l, layer in enumerate(dungeon)
        for r, row in enumerate(layer)
        for c in range(len(row))
        if row[c] == key
    )


def escape_minutes(
    dungeon: list[list[bytes]], start: tuple[int, int, int], target: tuple[int, int, int]
) -> int | None:
    """六方向 BFS 逐层扩展,返回 start→target 的最少分钟数;不可达返回 None。"""
    L, R, C = len(dungeon), len(dungeon[0]), len(dungeon[0][0])
    seen = {start}
    frontier = [start]  # 恰好用 minutes 分钟能站到的所有格子
    minutes = 0
    while frontier:
        nxt: list[tuple[int, int, int]] = []  # 再多走一分钟能站到的格子
        for l, r, c in frontier:
            if (l, r, c) == target:
                return minutes
            for dl, dr, dc in DIRS:
                nl, nr, nc = l + dl, r + dr, c + dc
                in_range = 0 <= nl < L and 0 <= nr < R and 0 <= nc < C  # 先判界,避免越界读
                can_step = in_range and dungeon[nl][nr][nc] != WALL and (nl, nr, nc) not in seen
                if can_step:
                    seen.add((nl, nr, nc))  # 入队即标记,同一个格子不会重复扩展
                    nxt.append((nl, nr, nc))
        frontier, minutes = nxt, minutes + 1
    return None


def solve() -> None:
    data = iter(sys.stdin.buffer.read().split())
    out: list[str] = []

    while True:
        L, R, C = int(next(data)), int(next(data)), int(next(data))
        if L == 0 and R == 0 and C == 0:  # 三个 0 表示输入结束
            break
        # 每层 R 行、每行 C 个字符;层与层之间的空行被 read().split() 当作空白丢掉
        dungeon = [[next(data) for _ in range(R)] for _ in range(L)]

        minutes = escape_minutes(dungeon, find(dungeon, START), find(dungeon, EXIT))
        out.append(f"Escaped in {minutes} minute(s)." if minutes is not None else "Trapped!")

    print('\n'.join(out))


if __name__ == "__main__":
    solve()

C++ 版(同一算法):

cpp
/**
 * Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
 * rbook: -> https://rbook.roj.ac.cn  https://rbook2.roj.ac.cn
 * rainboy的学习导航网站: https://idx.roj.ac.cn
 * create_at: 2026-10-04 21:00
 * update_at: 2026-10-04 21:00
 */
// main.cpp:三维迷宫 BFS 求 S 到 E 的最少分钟数;六方向(层±1 / 行±1 / 列±1)扩展。

#include <iostream>
#include <queue>
#include <string>
using namespace std;

typedef long long ll;

const int MAXL = 35; // L、R、C 都不超过 30,留一点冗余
const int MAXR = 35;
const int MAXC = 35;

char dungeon[MAXL][MAXR][MAXC]; // 三维网格,存原字符 '#' '.' 'S' 'E'

// 六个方向的 (层, 行, 列) 偏移:层 ±1 是上下,行 ±1 是前后,列 ±1 是左右
int dl[6] = { 1, -1, 0, 0, 0, 0 };
int dr[6] = { 0, 0, 1, -1, 0, 0 };
int dc[6] = { 0, 0, 0, 0, 1, -1 };

bool seen[MAXL][MAXR][MAXC]; // BFS 入队即标记,保证每格只展开一次

struct Pos {
    int l, r, c;
};

// 六方向 BFS:返回 start 到 target 的最少分钟数;不可达返回 -1
int bfs(int L, int R, int C, Pos start, Pos target) {
    queue<Pos> q;
    q.push(start);
    seen[start.l][start.r][start.c] = true;

    int minutes = 0;
    while (!q.empty()) {
        int sz = (int)q.size(); // 当前层(恰好 minutes 分钟可达)的格子数
        for (int i = 0; i < sz; i++) {
            Pos cur = q.front(); q.pop();
            if (cur.l == target.l && cur.r == target.r && cur.c == target.c) {
                return minutes;
            }
            for (int k = 0; k < 6; k++) {
                int nl = cur.l + dl[k];
                int nr = cur.r + dr[k];
                int nc = cur.c + dc[k];
                // 先判界,再判墙,再判未访问
                if (nl < 0 || nl >= L || nr < 0 || nr >= R || nc < 0 || nc >= C) continue;
                if (dungeon[nl][nr][nc] == '#') continue;
                if (seen[nl][nr][nc]) continue;
                seen[nl][nr][nc] = true; // 入队即标记
                q.push({nl, nr, nc});
            }
        }
        minutes++;
    }
    return -1; // S 与 E 不连通
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int L, R, C;
    while (cin >> L >> R >> C) {
        if (L == 0 && R == 0 && C == 0) break;

        Pos start{-1, -1, -1}, target{-1, -1, -1};
        for (int l = 0; l < L; l++) {
            for (int r = 0; r < R; r++) {
                string row;
                cin >> row;
                for (int c = 0; c < C; c++) {
                    dungeon[l][r][c] = row[c];
                    if (row[c] == 'S') start = {l, r, c};
                    else if (row[c] == 'E') target = {l, r, c};
                }
            }
        }

        // 每组数据前清空 seen 数组实际用到的范围(L、R、C 都 ≤ 30)
        for (int l = 0; l < L; l++)
            for (int r = 0; r < R; r++)
                for (int c = 0; c < C; c++)
                    seen[l][r][c] = false;

        int ans = bfs(L, R, C, start, target);
        if (ans >= 0) cout << "Escaped in " << ans << " minute(s)." << '\n';
        else cout << "Trapped!" << '\n';
    }

    return 0;
}