问题描述
在一个 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
题解分析
这道题的分类是一道简单题目,但是其涉及到的思想还是很有分析讨论价值的,可以归类为很经典的图的深度/广度优先搜索问题,但是在这之前,我们需要深刻理解题目含义,以及题目约束条件所带来的影响。
如果询问AI的话,他会告诉我们这个问题可以分成三个步骤进行,首先找到迷宫中代表出口的点所在的坐标,然后从出口开始进行深度优先搜索或者广度优先搜索,最后对于无法访问到的点进行统计。
简单看这似乎没有任何问题,这道题目就应该这样去做。但是仔细分析,这个思路存在很多潜在问题,比如出口可以到达的节点并不意味着该节点可以走向出口,有可能他们之间的路径是单方向的。同时出口到达不了的点也并不意味着该节点并不能够到达终点。
所以首先我们需要对这道题的题目条件进行分析,分析出我们可以用一个有向图对迷宫信息进行存储。 即:
"."表示该节点到其他各个方向都有有向边"U",D,L,R则表示它们只对上/下左/右有出边
根据以上信息我们就可以构建一个存储迷宫信息的有向图了,graph存储邻接表
for i in range(N):
for j in range(M):
graph[(i, j)] = []
# 普通地板和传送器的连接
if maze[i][j] == '.':
if i > 0: # 上
graph[(i, j)].append((i - 1, j))
if i < N - 1: # 下
graph[(i, j)].append((i + 1, j))
if j > 0: # 左
graph[(i, j)].append((i, j - 1))
if j < M - 1: # 右
graph[(i, j)].append((i, j + 1))
elif maze[i][j] == 'U' and i > 0: # 向上传送
graph[(i, j)].append((i - 1, j))
elif maze[i][j] == 'D' and i < N - 1: # 向下传送
graph[(i, j)].append((i + 1, j))
elif maze[i][j] == 'L' and j > 0: # 向左传送
graph[(i, j)].append((i, j - 1))
elif maze[i][j] == 'R' and j < M - 1: # 向右传送
graph[(i, j)].append((i, j + 1))
从出口进行搜索,需要将有向图转换成反向的,即如果A能够到达B,则应从B进行搜索能够搜到A
def convert_to_reverse_graph(graph):
reverse_graph = {}
for node, neighbors in graph.items():
if node not in reverse_graph:
reverse_graph[node] = []
for neighbor in neighbors:
if neighbor not in reverse_graph:
reverse_graph[neighbor] = []
reverse_graph[neighbor].append(node) # 反向边
return reverse_graph
最后从出口进行深度/广度优先搜索,利用visited存储访问过的节点即可。
def dfs(reverse_graph, start, visited):
stack = [start]
while stack:
current = stack.pop()
for neighbor in reverse_graph.get(current, []):
if neighbor not in visited:
visited.add(neighbor)
stack.append(neighbor)
没有出现在visited中的节点即无法到达出口的节点。
===
小白作者欢迎指教。