隐藏网格下的最小消耗路径

Submission

运行时间: 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))

                
        

Explain

这个题解利用了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

Explore

在DFS过程中,每个单元格访问前会检查其成本是否为无穷大(inf),这代表该单元格尚未被访问。一旦访问一个单元格,立即更新其成本,这样即使后续DFS尝试再次访问这个单元格,由于成本已被改变,将会跳过它。这种方法确保了每个单元格只被访问一次,防止了无限循环的发生。

在Dijkstra算法中,将不可移动方向的成本设为负无穷(-inf)实际上是表示这些单元格是不可达的。Dijkstra算法在更新路径成本时会检查新的路径成本是否比当前记录的成本低,如果遇到成本为负无穷的单元格,这个检查将确保这些单元格不会被加入到优先队列中。因此,这样的标记有助于防止算法尝试通过不可达的路径寻找目标。

在Dijkstra算法中,最小堆(Min-heap)用于存储所有待处理的单元格及其到起点的距离。每次从堆中取出(pop)一个元素时,都是当前堆中拥有最小成本的单元格。这确保了每一步算法都在扩展最低成本的路径。通过这种方式,算法可以有效地找到从起点到任一点的最短路径。

在DFS过程中,如果找到目标单元格,则会记录其位置。如果DFS完成后目标单元格的位置仍然是初始设置的无穷大值(inf),这表示目标单元格不在可达区域内。在Dijkstra算法执行中,如果堆已经空了(即没有更多单元格可处理)而目标单元格尚未被访问,这同样说明目标单元格不可达。在这两种情况下,函数返回-1,表明无法到达目标单元格。