<简单题>我好想逃却逃不掉|豆包MarsCode AI 刷题

466 阅读9分钟

1.题目

我好想逃却逃不掉

问题描述

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

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

——我好想逃。

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

你的任务是找出在迷宫的哪些位置,当玩家移动到此处时,无论接下来如何移动都无法再到达出口。


测试样例

样例1:

输入:N = 5, M = 5, data = [".....", ".RRD.", ".U.DR", ".ULL.", "....O"] 输出:10

样例2:

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

样例3:

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

这里并没有把题目讲明,下面是一些补充:

输入描述:

第一行输入两个整数n和m,表示迷宫的长和宽 接下来n行,每行m个字符,用于描述迷宫构造,每个字符可能为以下几种

  • . (小数点)表示空地,玩家在空地时可以选择往 [上,下,左,右] 中的某个方问移动一格

  • U, D, L, R 分别表示朝向 [上, 下,左, 右]的传送带,站在传送带上的人会被强制移动到其指向的下一个位置

    • 如果下一个位器还是传送带,会被继续传下去
    • 如果传送带指向迷宫外,玩家会撞在墙上昏过去,游戏结束
  • O (大写字母O) 表示迷宫出口

输出描述:

输出一个数字,表示迷宫中有多少个位置,当玩家处于此时,无论接下来如何移动都无法再到达出口 (传送带,空地,出口都算一个位置)

2.思路

DFS

先看一个测试样例感受一下题目:

以三行三列的迷宫为例,可以看到除出口外剩余8个位置都不能走到出口,均是撞在墙上,此外,还有一种情况是踩到了形成环的传送门。

即,有两种情况会导致出不去:

  1. 撞墙
  2. 踩到了形成环的传送门

用二维数组表示一个n行m列的迷宫,下面用x表示行,用y表示列

2.1方向的表示

key是U D L R,value是一个元组,表示下一个点应该怎么走

directions = {
        'U': (-1, 0),   # 上
        'D': (1, 0),    # 下
        'L': (0, -1),   # 左
        'R': (0, 1)     # 右
    }

2.2递归函数 can_reach_exit

can_reach_exit(start_x, start_y, visited)

尝试从指定起点 (start_x, start_y) 到达出口 O

2.2.1撞墙

即判断当前位置是否超出迷宫边界

def is_valid(x, y):
        return 0 <= x < N and 0 <= y < M
if not is_valid(start_x, start_y):
        return False

2.2.2形成环

用一个集合visited存放已经访问过的位置,数组中存放当前坐标位置的元组,若某位置已访问,说明形成环,该点无法到达出口

 # 如果已经访问过,说明形成了环
if (start_x, start_y) in visited:
    return False

2.2.3到达出口

该点等于'O',表示可以到达出口

if maze[start_x][start_y] == 'O':             
    return True

2.2.4将该点标记为已访问

visited.add((start_x, start_y))

2.2.5传送带

如果是传送带点,按照key取出对应的元组,取出的x,y与当前x,y相加,可以得到下一个到达的点,再去判断这个点是否可以到达出口

# 如果是传送点if maze[start_x][start_y] in directions:             dx, dy = directions[maze[start_x][start_y]]             next_x, next_y = start_x + dx, start_y + dy             return can_reach_exit(next_x, next_y, visited)

2.2.6普通点

  • for dx, dy in directions.values():表示尝试上下左右四个方向
  • 由于此处会尝试四个方向,所以要使用visited.copy(),每个方向的递归分支都得到一个独立的 visited 集合,不会影响其他方向的访问记录,这避免了一个方向的访问状态影响其他方向的递归路径
  • 如果下一个位置不越界,而且可以出去,返回ture
 # 如果是普通点,尝试四个方向
        for dx, dy in directions.values():
            next_x, next_y = start_x + dx, start_y + dy
            if is_valid(next_x, next_y) and can_reach_exit(next_x, next_y, visited.copy()):
                return True

其余情况下都出不去,在最后return False

2.3计算无法到达出口的位置数量

visited是一个集合,不能重复

 # 统计无法到达出口的位置数量
    count = 0
    for i in range(N):
        for j in range(M):
            if maze[i][j] != 'O':   # 不考虑出口本身
                if not can_reach_exit(i, j, set()):
                    count += 1
    
    return count

3.代码

3.1我的代码

def solution(N, M, data):
    # Edit your code here
    # 用一个字典存放方向
    directions = {
        'U': (-1, 0),
        'D': (1, 0),
        'L': (0, -1),
        'R': (0, 1)
    }
    # 定义函数判断当前点是否越界
    def is_valid(start_x, start_y):
        return 0 <= start_x <= N and 0 <= start_y <=M
    def can_reach_exit(start_x, start_y, visited):
        # 越界的点不能到达出口
        if not is_valid(start_x, start_y):
            return False
        # 如果走到已经走过的点,则说明形成环,不能到达出口
        if (start_x, start_y) in visited:
            return False
        # 如果这个点是O,则说明可以到达出口
        # 注意访问二维数组的方式
        # if data[start_x, start_y] == 'O':
        if data[start_x][start_y] == 'O':
            return True
        # 将这个点加入已访问的集合
        visited.append((start_x, start_y))
        # 如果这个点是传送带
        if data[start_x][start_y] in directions:
            dx, dy = directions[data[start_x][start_y]]
            next_x, next_y = start_x + dx, start_y + dy
            is_valid(next_x, next_y, visited)
        # 如果是普通点,尝试所有方向
        else:
            for dx, dy in directions.values():
                next_x, next_y = start_x + dx, start_y + dy
                # 如果下一个点不越界且下一个点能走出去,则返回Ture
                if is_valid(next_x, next_y) and can_reach_exit(next_x, next_y, visited.copy()):
                    return True
        # 其余情况都不能走出去
        return False
    # 遍历每个点,找出走不出去的点
    count = 0
    for i in range(N):
        for j in range(M):
            if can_reach_exit(i, j, set()):
                count += 1
    return count

if __name__ == "__main__":
    # Add your test cases here
    pattern = [
        [".",  ".", ".", ".", "."],
        [".",  "R", "R", "D", "."],
        [".", "U", ".", "D", "R"],
        [".", "U", "L", "L", "."],
        [".", ".", ".", ".", "O"]
    ]
    print(solution(5, 5, pattern) == 10)
  1. 注意访问二维数组的方式,不是if data[start_x, start_y] == 'O':,而是 if data[start_x][start_y] == 'O':
  2. 集合使用add添加元素

[ERROR] if data[start_x][start_y] == 'O':

[ERROR] ~~~~^^^^^^^^^

[ERROR] IndexError: list index out of range

这是

def is_valid(start_x, start_y):
        return 0 <= start_x <= N and 0 <= start_y <=M

这个函数出了问题,不能等于N和M

[ERROR] is_valid(next_x, next_y, visited)

[ERROR] TypeError: solution..is_valid() takes 2 positional arguments but 3 were given

if data[start_x][start_y] in directions:
            dx, dy = directions[data[start_x][start_y]]
            next_x, next_y = start_x + dx, start_y + dy
            is_valid(next_x, next_y, visited)

这个地方把函数名写错了

而且,此处can_reach_exit(next_x, next_y, visited)需要return

if can_reach_exit(i, j, set()):
                count += 1

计算出不去的点,应该是not can_reach_exit

def solution(N, M, data):
    # Edit your code here
    # 用一个字典存放方向
    directions = {
        'U': (-1, 0),
        'D': (1, 0),
        'L': (0, -1),
        'R': (0, 1)
    }
    # 定义函数判断当前点是否越界
    def is_valid(start_x, start_y):
        return 0 <= start_x < N and 0 <= start_y <M
    def can_reach_exit(start_x, start_y, visited):
        # 越界的点不能到达出口
        if not is_valid(start_x, start_y):
            return False
        # 如果走到已经走过的点,则说明形成环,不能到达出口
        if (start_x, start_y) in visited:
            return False
        # 如果这个点是O,则说明可以到达出口
        # 注意访问二维数组的方式
        # if data[start_x, start_y] == 'O':
        if data[start_x][start_y] == 'O':
            return True
        # 将这个点加入已访问的集合
        visited.add((start_x, start_y))
        # 如果这个点是传送带
        if data[start_x][start_y] in directions:
            dx, dy = directions[data[start_x][start_y]]
            next_x, next_y = start_x + dx, start_y + dy
            return can_reach_exit(next_x, next_y, visited)
        # 如果是普通点,尝试所有方向
        else:
            for dx, dy in directions.values():
                next_x, next_y = start_x + dx, start_y + dy
                # 如果下一个点不越界且下一个点能走出去,则返回Ture
                if is_valid(next_x, next_y) and can_reach_exit(next_x, next_y, visited.copy()):
                    return True
        # 其余情况都不能走出去
        return False
    # 遍历每个点,找出走不出去的点
    count = 0
    for i in range(N):
        for j in range(M):
            if not can_reach_exit(i, j, set()):
                count += 1
    return count

if __name__ == "__main__":
    # Add your test cases here
    pattern = [
        [".",  ".", ".", ".", "."],
        [".",  "R", "R", "D", "."],
        [".", "U", ".", "D", "R"],
        [".", "U", "L", "L", "."],
        [".", ".", ".", ".", "O"]
    ]
    print(solution(5, 5, pattern) == 10)

3.2参考代码

def solution(N, M, data):
    # 构建迷宫矩阵
    maze = []
    for row in data:
        if isinstance(row, list):
            maze.append(row)
        else:
            maze.append(list(row))
    
    # 方向映射
    directions = {
        'U': (-1, 0),  # 上
        'D': (1, 0),   # 下
        'L': (0, -1),  # 左
        'R': (0, 1)    # 右
    }
    
    def is_valid(x, y):
        return 0 <= x < N and 0 <= y < M
    
    def can_reach_exit(start_x, start_y, visited):
        # 越界检查
        if not is_valid(start_x, start_y):
            return False
            
        # 如果已经访问过,说明形成了环
        if (start_x, start_y) in visited:
            return False
            
        # 到达出口
        if maze[start_x][start_y] == 'O':
            return True
            
        # 记录当前位置为已访问
        visited.add((start_x, start_y))
        
        # 如果是传送点
        if maze[start_x][start_y] in directions:
            dx, dy = directions[maze[start_x][start_y]]
            next_x, next_y = start_x + dx, start_y + dy
            return can_reach_exit(next_x, next_y, visited)
        
        # 如果是普通点,尝试四个方向
        for dx, dy in directions.values():
            next_x, next_y = start_x + dx, start_y + dy
            if is_valid(next_x, next_y) and can_reach_exit(next_x, next_y, visited.copy()):
                return True
                
        return False
    
    # 统计无法到达出口的位置数量
    count = 0
    for i in range(N):
        for j in range(M):
            if maze[i][j] != 'O':  # 不考虑出口本身
                if not can_reach_exit(i, j, set()):
                    count += 1
    
    return count


if __name__ == "__main__":
    # Add your test cases here
    pattern = [
        [".",  ".", ".", ".", "."],
        [".",  "R", "R", "D", "."],
        [".", "U", ".", "D", "R"],
        [".", "U", "L", "L", "."],
        [".", ".", ".", ".", "O"]
    ]
    print(solution(5, 5, pattern) == 10)

4.参考资料