我好想逃却逃不掉 | 豆包MarsCode AI刷题

286 阅读4分钟

算法分析:我好想逃却逃不掉

问题描述

曾经的我不过是一介草民,混迹市井,默默无名。直到我被罗马的士兵从家乡捉走丢进竞技场......竞技场中,工程师们设计了复杂的迷宫。迷宫地上布满了传送机关,玩家一旦踩上去会被强制传送到指定位置。
这些传送机关有时会构成一个闭环,让玩家反复传送,永远无法逃脱。因此,我们需要找到迷宫中所有的位置,这些位置无论如何移动都无法到达出口。

输入格式

  1. 第一行输入两个整数 nm,表示迷宫的长和宽。
  2. 接下来的 n 行,每行有 m 个字符,描述迷宫的构造。
    • . 表示空地,玩家可以选择上下左右移动一格。
    • UDLR 分别表示朝上、下、左、右的传送带,站在上面会被强制移动到下一个位置。
    • O 表示出口。

输出格式

输出一个数字,表示迷宫中有多少个位置无论如何移动都无法到达出口。

数据范围

1 ≤ n, m ≤ 10^5,且 1 ≤ n × m ≤ 10^5。
保证每个迷宫中有且仅有一个出口。


思路与算法设计

要解决该问题,我们需要判断哪些位置可以通过传送带或者移动最终到达出口,进而反推出不可达的位置。结合迷宫的特性,我们采用以下方法:

  1. 从出口反向搜索
    以出口为起点,利用深度优先搜索(DFS)或者广度优先搜索(BFS),标记所有可以从出口到达的位置。
  2. 处理传送带逻辑
    如果当前位置是传送带,则直接跳转到传送带的目标位置继续标记。传送带的链式跳转需特别注意,避免无限循环。
  3. 统计不可达位置
    遍历整个迷宫,统计未被标记为可达的位置数量。

这种反向搜索的方式能够有效避免不必要的重复计算,并确保算法复杂度为 O(n × m),适合大规模数据。


算法实现

以下是完整的 Python 实现:

def solution(n, m, maze):
    def dfs(x, y):
        # 判断是否越界或已访问
        if x < 0 or x >= n or y < 0 or y >= m or visited[x][y]:
            return
        visited[x][y] = True

        # 如果当前是空地,继续搜索四个方向
        if maze[x][y] == '.':
            for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
                dfs(x + dx, y + dy)
        # 如果当前是传送带,直接跳转到目标位置
        elif maze[x][y] == 'U' and x > 0:
            dfs(x - 1, y)
        elif maze[x][y] == 'D' and x < n - 1:
            dfs(x + 1, y)
        elif maze[x][y] == 'L' and y > 0:
            dfs(x, y - 1)
        elif maze[x][y] == 'R' and y < m - 1:
            dfs(x, y + 1)

    # 找到出口位置
    exit_x, exit_y = -1, -1
    for i in range(n):
        for j in range(m):
            if maze[i][j] == 'O':
                exit_x, exit_y = i, j
                break
        if exit_x != -1:
            break

    # 初始化访问状态
    visited = [[False] * m for _ in range(n)]
    dfs(exit_x, exit_y)

    # 统计不可达位置数量
    unreachable_count = 0
    for i in range(n):
        for j in range(m):
            if not visited[i][j]:
                unreachable_count += 1

    return unreachable_count


# 测试样例
if __name__ == "__main__":
    maze = [
        ['.', '.', '.', '.', '.'],
        ['.', 'R', 'R', 'D', '.'],
        ['.', 'U', '.', 'D', 'R'],
        ['.', 'U', 'L', 'L', '.'],
        ['.', '.', '.', '.', 'O']
    ]
    print(solution(5, 5, maze))  # 输出:10

算法分析

时间复杂度

  1. 每个位置最多被访问一次,因此 DFS 的总时间复杂度为 O(n × m)。
  2. 遍历迷宫统计不可达位置的复杂度也为 O(n × m)。
    综上,算法的整体时间复杂度为 O(n × m)。

空间复杂度

我们需要一个 visited 数组来记录每个位置的访问状态,其空间复杂度为 O(n × m)。

优化点

  1. 避免重复访问
    通过 visited 数组,确保每个位置仅被访问一次,避免死循环。
  2. 直接跳转传送带
    如果当前位置是传送带,可以直接跳转到目标位置,而不需要额外处理路径。

总结与反思

通过该题目,我们学习了如何结合迷宫特点设计高效的算法。

值得注意的点:

  • 从出口反向搜索的思路:相比从任意点出发试图找到出口,反向搜索能够减少冗余计算。
  • 传送带逻辑处理:明确传送带跳转的优先级,避免循环传送导致的死循环。

改进方向:

若进一步优化,可以尝试使用队列实现 BFS,使得搜索逻辑更简洁,同时更适合处理大规模迷宫中的复杂传送逻辑。

该算法不仅适用于当前问题,还可以推广到类似的迷宫逃脱问题,为解题提供了重要的思路和方法。