标签: 深度优先搜索 广度优先搜索 图 交互 堆(优先队列)
难度: Medium
标签: 深度优先搜索 广度优先搜索 图 交互 堆(优先队列)
难度: Medium
运行时间: 596 ms
内存: 25.8 MB
# """ # This is GridMaster's API interface. # You should not implement it, or speculate about its implementation # """ #class GridMaster(object): # def canMove(self, direction: str) -> bool: # # # def move(self, direction: str) -> int: # # # def isTarget(self) -> None: # # class Solution(object): def findShortestPath(self, master: 'GridMaster') -> int: dirs = 'DLRU' dists = [[inf]*201 for _ in range(201)] costs = [[inf]*201 for _ in range(201)] dr = dc = inf def dfs(r, c): nonlocal dr, dc if master.isTarget(): dr, dc = r, c for di, (nr, nc) in enumerate([(r+1, c), (r, c-1), (r,c+1), (r-1,c)]): d = dirs[di] if costs[nr][nc] != inf: continue if not master.canMove(d): costs[nr][nc] = -inf else: costs[nr][nc] = master.move(d) dfs(nr, nc) master.move(dirs[3-di]) sr = sc = 100 costs[100][100] = 0 dfs(100, 100) if dr == inf: return -1 h = [(0,sr, sc)] dists[sr][sc] = 0 while h: dist, r, c = heappop(h) if r == dr and c == dc: return dist for nr, nc in [(r+1, c), (r, c-1), (r,c+1), (r-1,c)]: cost = costs[nr][nc] if abs(cost) != inf and dists[nr][nc] > dist + cost: dists[nr][nc] = dist + cost heappush(h, (dist + cost, nr, nc))
这个题解利用了DFS和Dijkstra算法的结合来寻找从起点到目标的最短路径。首先,使用DFS从初始位置开始,探索所有可以移动到的单元格,同时记录每个单元格的移动成本和标记是否为目标。然后,使用Dijkstra算法从起点开始,根据已知的成本和距离,计算到每个可达单元格的最小路径成本。如果找到目标单元格,则返回到该单元格的最小成本。
时间复杂度: O(N + M log M)
空间复杂度: O(N)
# Solution class defining the method to find the shortest path using GridMaster's API class Solution(object): def findShortestPath(self, master: 'GridMaster') -> int: dirs = 'DLRU' # Directions for movement dists = [[inf]*201 for _ in range(201)] # Distance matrix costs = [[inf]*201 for _ in range(201)] # Cost matrix dr = dc = inf # Target row and column def dfs(r, c): nonlocal dr, dc if master.isTarget(): # Check if current cell is the target dr, dc = r, c for di, (nr, nc) in enumerate([(r+1, c), (r, c-1), (r,c+1), (r-1,c)]): d = dirs[di] if costs[nr][nc] != inf: continue # Skip if already visited if not master.canMove(d): costs[nr][nc] = -inf # Mark as impassable else: costs[nr][nc] = master.move(d) # Move and record cost dfs(nr, nc) # Recursively visit master.move(dirs[3-di]) # Move back sr = sc = 100 # Start at the center of the grid costs[100][100] = 0 # Starting point cost is zero dfs(100, 100) # Start DFS from the center if dr == inf: return -1 # Return -1 if target is not reachable h = [(0,sr, sc)] # Min-heap for Dijkstra's algorithm dists[sr][sc] = 0 # Starting point distance is zero while h: dist, r, c = heappop(h) # Pop the least cost cell if r == dr and c == dc: return dist # Return distance if target reached for nr, nc in [(r+1, c), (r, c-1), (r,c+1), (r-1,c)]: cost = costs[nr][nc] if abs(cost) != inf and dists[nr][nc] > dist + cost: dists[nr][nc] = dist + cost # Update distance heappush(h, (dist + cost, nr, nc)) # Push new distance
在DFS过程中,每个单元格访问前会检查其成本是否为无穷大(inf),这代表该单元格尚未被访问。一旦访问一个单元格,立即更新其成本,这样即使后续DFS尝试再次访问这个单元格,由于成本已被改变,将会跳过它。这种方法确保了每个单元格只被访问一次,防止了无限循环的发生。
在Dijkstra算法中,将不可移动方向的成本设为负无穷(-inf)实际上是表示这些单元格是不可达的。Dijkstra算法在更新路径成本时会检查新的路径成本是否比当前记录的成本低,如果遇到成本为负无穷的单元格,这个检查将确保这些单元格不会被加入到优先队列中。因此,这样的标记有助于防止算法尝试通过不可达的路径寻找目标。
在Dijkstra算法中,最小堆(Min-heap)用于存储所有待处理的单元格及其到起点的距离。每次从堆中取出(pop)一个元素时,都是当前堆中拥有最小成本的单元格。这确保了每一步算法都在扩展最低成本的路径。通过这种方式,算法可以有效地找到从起点到任一点的最短路径。
在DFS过程中,如果找到目标单元格,则会记录其位置。如果DFS完成后目标单元格的位置仍然是初始设置的无穷大值(inf),这表示目标单元格不在可达区域内。在Dijkstra算法执行中,如果堆已经空了(即没有更多单元格可处理)而目标单元格尚未被访问,这同样说明目标单元格不可达。在这两种情况下,函数返回-1,表明无法到达目标单元格。