算法巧思,轻松拿下最小路径和
2024-01-12 16:27:13
64. 最小路径和:动态规划的艺术
在算法的浩瀚海洋中,总有一些题目令人眼前一亮,激发我们的思考,让我们在解题的过程中不断精进。今天,我们来共同探讨 LeetCode 上的经典题目——64. 最小路径和 。
题目
我们给定一个由非负整数构成的 m x n 矩阵,代表一个从左上角到右下角的网格。在该网格中,从左上角到右下角的每个单元格都代表一个必须经过的障碍物。网格中的每个单元格的值表示通过该单元格所需的额外费用。
我们的目标是找到一条从左上角到右下角的路径,使得该路径上的总费用最小。
解题思路
乍一看,这道题似乎是一道典型的动态规划问题。动态规划是一种强大的算法设计范式,它将问题分解成子问题,并存储子问题的解,以避免重复计算。
对于最小路径和问题,我们不妨从动态规划的角度思考一下。
设 dp[i][j] 表示从左上角走到第 i 行第 j 列的最小路径和。根据动态规划的一般套路,我们可以得到如下递推公式:
dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j]
其中,grid[i][j] 表示第 i 行第 j 列的单元格值。
然而,细心观察,我们不难发现这个递推公式有一个潜在问题。如果我们按照这个递推公式进行计算,会导致我们每次只能向右或者向下移动一格。这显然不符合我们寻找最小路径和的要求,因为我们可能需要经过多个单元格才能到达右下角。
优化思路
为了解决这个问题,我们需要对递推公式进行优化。我们不再限制每次只能向右或者向下移动一格,而是考虑所有可能的路径,并选择其中费用最小的路径。
具体来说,我们修改后的递推公式如下:
dp[i][j] = min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]) + grid[i][j]
在这个公式中,我们额外考虑了从左上角走到第 i 行第 j 列之前一格的最小路径和 dp[i - 1][j - 1]。这样,我们就可以选择从三个方向中经过的路径中最小的一条。
代码实现
Python 代码实现如下:
def min_path_sum(grid):
m, n = len(grid), len(grid[0])
dp = [[0 for _ in range(n)] for _ in range(m)]
dp[0][0] = grid[0][0]
for i in range(1, m):
dp[i][0] = dp[i - 1][0] + grid[i][0]
for j in range(1, n):
dp[0][j] = dp[0][j - 1] + grid[0][j]
for i in range(1, m):
for j in range(1, n):
dp[i][j] = min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]) + grid[i][j]
return dp[m - 1][n - 1]
总结
解题的关键在于意识到我们可以从多个方向移动,并选择其中费用最小的路径。通过优化后的递推公式,我们成功解决了这个问题,并得到了最小路径和的正确解。
希望这篇文章能带给你算法思维的启发,下次遇到类似的问题时,你就能轻松应对啦!
常见问题解答
-
为什么需要对递推公式进行优化?
因为原始的递推公式限制了我们的移动方向,无法找到最小路径和。 -
优化后的递推公式考虑了哪些移动方向?
左、右、上三个方向。 -
代码中 dp 数组的作用是什么?
dp[i][j] 存储了从左上角走到第 i 行第 j 列的最小路径和。 -
代码中 for 循环是如何工作的?
外层循环负责遍历行,内层循环负责遍历列,并计算每个单元格的最小路径和。 -
如何使用代码解决最小路径和问题?
只需将输入网格 grid 传递给 min_path_sum() 函数,即可得到最小路径和。