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个位置都不能走到出口,均是撞在墙上,此外,还有一种情况是踩到了形成环的传送门。
即,有两种情况会导致出不去:
- 撞墙
- 踩到了形成环的传送门
用二维数组表示一个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)
- 注意访问二维数组的方式,不是
if data[start_x, start_y] == 'O':,而是if data[start_x][start_y] == 'O': - 集合使用
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)