题目:原题链接(困难)
标签:图、广度优先搜索、动态规划、深度优先搜索、记忆化递归
解法 | 时间复杂度 | 空间复杂度 | 执行用时 |
---|---|---|---|
Ans 1 (Python) | O ( N × M 2 + 2 N ) O(N×M^2+2^N) O(N×M2+2N) : 其中M为地图边长;N为机关数量 | O ( N 2 ) O(N^2) O(N2) | 7412ms (7.69%) |
Ans 2 (Python) | |||
Ans 3 (Python) |
解法一:
class Solution:
def __init__(self):
# 地图信息
self.maze = []
self.s1, self.s2 = 0, 0
# 地图内信息
self.gear = [] # 机关列表
self.stone = [] # 石碓列表
self.target = (-1, -1) # 目标位置
self.start = (-1, -1) # 起点
self.wall = set() # 墙
self.num1, self.num2 = 0, 0 # 机关数量、石碓数量
# 特殊点信息
self.special_list = []
self.special_dict = {}
# 机关点距离关系
self.distance1 = []
self.distance2 = []
self.distance3 = []
def _is_valid(self, x, y):
return 0 <= x < self.s1 and 0 <= y < self.s2 and (x, y) not in self.wall
def _get_neighbour(self, x, y):
return [(i, j) for (i, j) in [(x - 1, y), (x + 1, y), (x, y - 1), (x, y + 1)] if self._is_valid(i, j)]
def _initialize(self):
"""初始化地图:初始化后支持在任意两个特殊点之间计算距离"""
# 找到所有目标位置
for i in range(self.s1):
for j in range(self.s2):
if self.maze[i][j] == "S":
self.start = (i, j)
elif self.maze[i][j] == "T":
self.target = (i, j)
elif self.maze[i][j] == "O":
self.stone.append((i, j))
elif self.maze[i][j] == "M":
self.gear.append((i, j))
elif self.maze[i][j] == "#":
self.wall.add((i, j))
# 计算机关数量和石碓数量
self.num1 = len(self.gear)
self.num2 = len(self.stone)
# 生成特殊点列表
self.special_list = [self.start] + self.stone + self.gear + [self.target]
self.special_dict = {point: i for i, point in enumerate(self.special_list)}
# 计算所有特殊点之间的距离
size = len(self.special_list)
self.distance = [[float("inf")] * size for _ in range(size)]
for i in range(size):
point = self.special_list[i]
# 生成目标列表
targets = {self.special_list[j]: j for j in range(i + 1, size)}
# 广度优先搜索
queue = collections.deque([point])
visited = {point}
step = 0
while queue:
step += 1
for _ in range(len(queue)):
(i1, j1) = queue.popleft()
for (i2, j2) in self._get_neighbour(i1, j1):
if (i2, j2) in targets:
self.distance[i][targets[(i2, j2)]] = step
self.distance[targets[(i2, j2)]][i] = step
del targets[(i2, j2)]
if (i2, j2) not in visited:
queue.append((i2, j2))
visited.add((i2, j2))
if not targets:
break
def _get_distance(self, point1, point2):
"""计算两个特殊点之间的距离:默认两个点均为特殊点"""
return self.distance[self.special_dict[point1]][self.special_dict[point2]]
def minimalSteps(self, maze: List[str]) -> int:
self.s1, self.s2 = len(maze), len(maze[0])
self.maze = maze
# 初始化地图
self._initialize()
# 处理没有机关的情况
if not self.gear:
res = self._get_distance(self.start, self.target)
return res if res != float("inf") else -1
# 计算从起点开始到达每个机关的距离(中间搬一次石头)
self.distance1 = [float("inf")] * self.num1
for i in range(self.num1):
for j in range(self.num2):
res = self._get_distance(self.start, self.stone[j]) + self._get_distance(self.stone[j], self.gear[i])
self.distance1[i] = min(self.distance1[i], res)
# 计算从每个机关到达另一个机关的距离(中间搬一次石头)
self.distance2 = [[float("inf")] * self.num1 for _ in range(self.num1)]
for i1 in range(self.num1):
for i2 in range(i1 + 1, self.num1):
for j in range(self.num2):
res = (self._get_distance(self.gear[i1], self.stone[j]) +
self._get_distance(self.stone[j], self.gear[i2]))
self.distance2[i1][i2] = self.distance2[i2][i1] = min(self.distance2[i1][i2], res)
# 计算从每个机关开始到达终点的距离
self.distance3 = [self._get_distance(self.gear[i], self.target) for i in range(self.num1)]
# 记忆化递归计算最终结果
ans = min(self.dfs(i, (1 << i)) + self.distance1[i] for i in range(self.num1))
return ans if ans != float("inf") else -1
@functools.lru_cache(None)
def dfs(self, now, state):
"""深度优先搜索(记忆化递归):now=当前所在机关位置;state=当前状态"""
# 处理已经触发所有机关的情况
if state == (1 << len(self.gear)) - 1:
return self.distance3[now]
# 处理还没有触发所有机关的情况
res = float("inf")
for i in range(self.num1): # 遍历所有可以移动的下一个机关
if state & (1 << i) == 0 and self.distance2[now][i] != float("inf"):
res = min(res, self.dfs(i, state | (1 << i)) + self.distance2[now][i])
return res