每日一题:我好想逃却逃不掉 | 豆包MarsCode AI刷题

206 阅读6分钟

问题描述

曾经的我不过是一介草民,混迹市井,默默无名。直到我被罗马的士兵从家乡捉走丢进竞技场……

对手出现了,我架紧盾牌想要防御,只觉得巨大的冲击力有如一面城墙冲涌而来,击碎了我的盾牌,我两眼发昏,沉重的身躯轰然倒地。

——我好想逃。

但罗马最大的竞技场,哪有这么容易逃得掉。工程师们早就在地上装了传送机关,虽不会伤人,却会将站在上面的人传到它指向的位置。若是几个传送机关围成一个环,不小心踩在上面的人就会被一圈圈地反复传送……想到这里,我不由得打了个寒颤。必须避开这些危险的地方!

在一个 N × M 的竞技场迷宫中,你的任务是找出在迷宫中,所有"危险位置"的数量。 "危险位置"定义为:如果站在该位置上,无论采取什么移动策略,都无法到达出口

竞技场中包含以下几种元素:

  • .:表示普通地板,可以自由移动到上下左右相邻的格子(不可以走斜线)
  • O:表示出口
  • U:表示向上的传送器,踩上去会被强制传送到上方的格子
  • D:表示向下的传送器,踩上去会被强制传送到下方的格子
  • L:表示向左的传送器,踩上去会被强制传送到左方的格子
  • R:表示向右的传送器,踩上去会被强制传送到右方的格子

注意,如果被传送出了竞技场之外,则算作死亡。


输入参数

  • N: 一个整数,表示竞技场地图的行数
  • M: 一个整数,表示竞技场地图的列数
  • data: 一个字符二维数组,表示竞技场地板地图。数组大小为 N × M,其中 1 ≤ N, M ≤ 100

测试样例

样例1:

输入:N = 5, M = 5, data = [ [".", ".", ".", ".", "."], [".", "R", "R", "D", "."], [".", "U", ".", "D", "R"], [".", "U", "L", "L", "."], [".", ".", ".", ".", "O"] ] 输出:10 解释:存在 10 个位置,如果站在这些位置上,将永远无法到达右下角的出口(用 X 标记): ['.', '.', '.', '.', '.'] ['.', 'X', 'X', 'X', '.'] ['.', 'X', 'X', 'X', 'X'] ['.', 'X', 'X', 'X', '.'] ['.', '.', '.', '.', 'O']

样例2:

输入:N = 4, M = 4, data = [[".", "R", ".", "O"], ["U", ".", "L", "."], [".", "D", ".", "."], [".", ".", "R", "D"]] 输出:2

样例3:

输入:N = 3, M = 3, data = [[".", "U", "O"], ["L", ".", "R"], ["D", ".", "."]] 输出:8

解题思路可以分为以下几个步骤:


1. 寻找迷宫出口

首先,代码遍历整个迷宫(一个二维数组 data),找到表示出口的字符 'O' 的坐标 (exitX, exitY)

  • 为什么要找到出口? 出口是迷宫中最关键的位置,所有路径的可达性都基于从出口向外扩散的探索。

2. 深度优先搜索 (DFS) 标记可达路径

使用深度优先搜索(DFS)从出口出发,标记所有可以到达的迷宫位置:

  • 基本规则

    • 如果当前位置是出口,或通过其他路径可达,就继续探索周围四个方向(上、下、左、右)。
    • 每个位置只能访问一次(用 visited 数组记录访问状态)。
    • 如果当前位置符合规则(比如周围的方向是合法的通路 'D', 'U', 'L', 'R', '.'),就递归地检查下一步。
  • 特殊通路规则

    : 迷宫中某些字符限制了移动方向:

    • 'D' 允许从当前位置向下移动。
    • 'U' 允许从当前位置向上移动。
    • 'L' 允许从当前位置向左移动。
    • 'R' 允许从当前位置向右移动。
    • '.' 表示无方向限制,可以自由通过。

    每次递归时都会检查这些条件,确保路径探索的正确性。


3. 标记可达位置

DFS 中的 canWalk 数组用于标记哪些位置是从出口可以到达的。

  • 初始化时,所有位置默认不可达(False),除了出口位置(True)。
  • 每访问一个符合条件的格子,就将其标记为 True,并继续递归探索。

4. 统计不可达位置

在完成整个迷宫的 DFS 遍历后,代码再次遍历整个迷宫,通过检查 canWalk 数组中值为 False 的格子数量,统计 不可达位置 的总数。


关键逻辑的总结

  1. 出口是起点,从出口出发,反向确定所有可以到达的位置。
  2. 深度优先搜索模拟了人从出口开始“走迷宫”的过程,符合迷宫通行规则的路径会被标记为可达。
  3. 最终,通过对不可达位置数量的统计,得出答案。

代码的思维图解

以示例迷宫为例:

.  .  .  .  .
.  R  R  D  .
.  U  .  D  R
.  U  L  L  .
.  .  .  .  O
  • 步骤 1:找到出口位置 (4, 4)

  • 步骤 2

    :从出口开始进行 DFS:

    • 出口位置 (4, 4) 标记为可达。
    • 检查 (4, 3) 是否符合条件,发现它是 '.',继续递归标记。
    • 继续递归向出口四周扩展,直到所有可以到达的位置都被标记。
  • 步骤 3:统计 canWalk 数组中标记为 False 的格子数量。结果为 10,即有 10 个位置无法到达出口。


复杂度分析

  1. 时间复杂度

    • 遍历迷宫寻找出口:O(N * M)
    • 深度优先搜索:最多访问每个格子一次,因此时间复杂度为 O(N * M)
    • 统计不可达位置:O(N * M)。 总计时间复杂度为 O(N * M)
  2. 空间复杂度

    • 需要额外的 visitedcanWalk 数组,大小均为 N * M
    • 递归调用栈的深度最大为迷宫大小,空间复杂度为 O(N * M)

代码实现

def solution(N, M, data):
    def dfs(x, y, can_walk):
        # 判断是否在迷宫内和访问过
        if x < 0 or x >= N or y < 0 or y >= M:  # 边界条件
            return
        if visited[x][y]:  # 如果已经访问过
            return
​
        visited[x][y] = True
        can_walk[x][y] = True  # 标记当前位置为可到达
​
        # 向上检查
        if x - 1 >= 0 and (data[x - 1][y] == 'D' or data[x - 1][y] == '.'):
            dfs(x - 1, y, can_walk)
        # 向下检查
        if x + 1 < N and (data[x + 1][y] == 'U' or data[x + 1][y] == '.'):
            dfs(x + 1, y, can_walk)
        # 向左检查
        if y - 1 >= 0 and (data[x][y - 1] == 'R' or data[x][y - 1] == '.'):
            dfs(x, y - 1, can_walk)
        # 向右检查
        if y + 1 < M and (data[x][y + 1] == 'L' or data[x][y + 1] == '.'):
            dfs(x, y + 1, can_walk)
​
    exit_x, exit_y = 0, 0
    found_exit = False
​
    # 找到迷宫出口
    for i in range(N):
        for j in range(M):
            if data[i][j] == 'O':
                exit_x, exit_y = i, j
                found_exit = True
                break
        if found_exit:
            break
​
    visited = [[False] * M for _ in range(N)]
    can_walk = [[False] * M for _ in range(N)]
    can_walk[exit_x][exit_y] = True  # 标记出口为可达
    dfs(exit_x, exit_y, can_walk)
​
    # 统计无法到达出口的位置数量
    unreachable_count = 0
    for i in range(N):
        for j in range(M):
            if not can_walk[i][j]:
                unreachable_count += 1
​
    return unreachable_count
​
​
if __name__ == "__main__":
    pattern = [
        ['.', '.', '.', '.', '.'],
        ['.', 'R', 'R', 'D', '.'],
        ['.', 'U', '.', 'D', 'R'],
        ['.', 'U', 'L', 'L', '.'],
        ['.', '.', '.', '.', 'O']
    ]
    print(solution(5, 5, pattern) == 10)
​