📅  最后修改于: 2023-12-03 15:07:16.629000             🧑  作者: Mango
在一个 m × n
的网格中,每个单元格有一个初始值 grid[i][j]
。我们从左上角的网格 grid[0][0]
开始出发,希望到达位于右下角的网格 grid[m-1][n-1]
,我们可以移动到相邻的四个方向之一(上、下、左或右),每次移动消耗的代价为当前单元格的值。我们希望在限制总代价的情况下,到达目的地的最小初始代价。 在这个题目中,网格中的起点和终点都是给定的。
例如,考虑下面的地图。
[1, 4, 4, 0]
[5, 3, 1, 5]
[2, 10, 10, 8]
[2, 10, 10, 0]
从左上角到右下角的最小代价路径是 1 -> 3 -> 1 -> 0 -> 0
,因此最低的初始代价为 4
。
首先我们可以使用深度优先搜索(DFS)来求解最小初始代价。具体来说,我们从左上角开始进行搜索,每当到达一个新的位置时,我们可以向右或者向下移动。如果我们到达了右下角,我们记录下路径所经过的最小代价,如果它比我们之前找到的最小初始代价更小,我们就需要更新最小初始代价。我们可以将这个过程看作对一棵赋权有向无环图进行 DFS 的过程,而我们需要求解的答案就是从左上角到右下角的一条最小权值路径。
但是该解法时间复杂度较高,不适用于大规模数据。
我们可以使用二分查找,查找一个最小的能够到达终点的非负代价。
具体来说,我们可以发现如果一条路径的代价为 x
,那么它至少需要保证每个格子的值与它的代价乘积之和不超过 x
。因此我们可以二分查找最小的 x
,满足它可以到达终点。
对于当前的 x
,我们可以利用动态规划的方法计算出从左上角到当前位置的代价不超过 x
的最小初始代价,其中动态规划的状态表示为 (i, j)
,表示从左上角出发到位置 (i, j)
的代价不超过 x
的最小初始代价。状态转移可以用标准的动态规划方法处理。
最终,我们得到的答案即为最小的满足要求的代价 x
。
下面是基于二分查找和动态规划的解法代码:
from typing import List
class Solution:
def minimumEffortPath(self, heights: List[List[int]]) -> int:
n, m = len(heights), len(heights[0])
left, right = 0, 10**6 # 代价为 10^6 是已知满足要求的代价上界
while left < right:
mid = (left + right) // 2
if self.check(mid, heights, n, m):
right = mid
else:
left = mid + 1
return left
def check(self, costLimit: int, heights: List[List[int]], n: int, m: int) -> bool:
queue = [(0, 0)]
visited = {(0, 0)}
while queue:
x, y = queue.pop(0)
if x == n - 1 and y == m - 1:
return True
for dx, dy in [(0, 1), (1, 0), (0, -1), (-1, 0)]:
nx, ny = x + dx, y + dy
if nx < 0 or nx >= n or ny < 0 or ny >= m:
continue
if (nx, ny) in visited:
continue
diff = abs(heights[nx][ny] - heights[x][y])
if diff > costLimit:
continue
queue.append((nx, ny))
visited.add((nx, ny))
return False
该算法的时间复杂度为 $O(nm \log C)$,其中 $C$ 表示整个网格中的所有节点的最大高度差,空间复杂度为 $O(nm)$。