跳到正文
第 64 题中等

最小路径和

Minimum Path Sum

在非负整数网格中从左上角走到右下角,每次只能向下或向右,求路径上的最小数字和。

在力扣中练习

题目描述

给定一个包含非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。

说明:每次只能向下或者向右移动一步。

 

示例 1:

输入:grid = [[1,3,1],[1,5,1],[4,2,1]]
输出:7
解释:因为路径 1→3→1→1→1 的总和最小。

示例 2:

输入:grid = [[1,2,3],[4,5,6]]
输出:12

 

提示:

  • m == grid.length
  • n == grid[i].length
  • 1 <= m, n <= 200
  • 0 <= grid[i][j] <= 200

解题思路

主解法

二维 DP · 累计路径和

到达每格的最小和,等于两条来路中较小的那个再加上当前格

路径总数是指数级的,逐条比较不可行。但到达 (i,j) 的最后一步仍然只有两种:从上面下来,或从左边过来。

设 dp[i][j] 为到达该格的最小路径和:

dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + grid[i][j]

注意选择的是来源的累计和,最后才把当前格的值加上去,而不是先加再比较。

自拟主例的网格第一行是 3,1,4,2,第一列是 3,2,4;它们没有选择,只能一路累加。内部格子才开始出现两条来路,例如 (2,2) 从上方 9+6=15、从左方 11+6=17 中取 15。

分步图解

(1,1):从上方来是 4 + 5 = 9,从左方来是 5 + 5 = 10,取 9。

i
1
j
1
最小路径和 · 上方与左方取较小写入答案

dp[i][j]:从左上角走到 (i,j) 的最小路径和

i \ j01230123初dp[0][0]:34dp[0][1]:4,选中的来源8dp[0][2]:810dp[0][3]:105dp[1][0]:5,候选来源9dp[1][1]:9,当前目标·dp[1][2]:尚未计算·dp[1][3]:尚未计算9dp[2][0]:9·dp[2][1]:尚未计算·dp[2][2]:尚未计算·dp[2][3]:尚未计算dp[0][1] → dp[1][1]:从上方dp[1][0] → dp[1][1]:从左方
dp[1][1]=min(dp[0][1] + grid[1][1], dp[1][0] + grid[1][1])
=min(4 + 5, 5 + 5)=9

选中 dp[0][1] 从上方

未计算初值
1 / 3
第二行开始出现两个来源;当前格自身的值要分别加到两条候选上。

边界是累加而不是全 1:dp[i][0] = dp[i-1][0] + grid[i][0],第一行同理。

分步图解

(2,2):从上方来是 9 + 6 = 15,从左方来是 11 + 6 = 17,取 15。

i
2
j
2
最小路径和 · 上方与左方取较小写入答案

dp[i][j]:从左上角走到 (i,j) 的最小路径和

i \ j01230123初dp[0][0]:34dp[0][1]:48dp[0][2]:810dp[0][3]:105dp[1][0]:59dp[1][1]:99dp[1][2]:9,选中的来源12dp[1][3]:129dp[2][0]:911dp[2][1]:11,候选来源15dp[2][2]:15,当前目标·dp[2][3]:尚未计算dp[1][2] → dp[2][2]:从上方dp[2][1] → dp[2][2]:从左方
dp[2][2]=min(dp[1][2] + grid[2][2], dp[2][1] + grid[2][2])
=min(9 + 6, 11 + 6)=15

选中 dp[1][2] 从上方

未计算初值
1 / 3
右下角取较小的上方 12+1=13;回溯时每一步都选较小的来源。

时间 O(m × n),空间 O(m × n)。

时间 O(m × n)空间 O(m × n)

易错点

  • 先比较 grid 本身而不是 dp:路径和是累计量,必须比较到达来源的累计和。
  • 边界也初始化为 1:第一行/第一列只能单向累加,值并不相等。
  • 忘记加 grid[i][j]:只取了来源的较小值,当前格被漏掉。
对照解法

滚动数组 · 一维 DP

一行一行覆盖,dp[j] 覆盖前是上方,dp[j-1] 是本行左方

时间相同,空间从 O(m × n) 降到 O(n);代价是无法直接回溯路径,需要额外记录来源。

转移只依赖上一行和本行左边,所以保留一行就够。

每行开始时,dp[0] 要先加上当前行第一列的 grid[i][0],因为第一列只能从上方来。然后 j 从左往右:

dp[j] = min(dp[j], dp[j-1]) + grid[i][j]

此时 dp[j] 还没被覆盖,是上一行的上方值;dp[j-1] 已经更新,是本行左方值。

时间 O(m × n),空间 O(n)。

  1. 1dp[0] = grid[0][0],第一行累加。
  2. 2每行先更新 dp[0] += grid[i][0]。
  3. 3j 从左到右:dp[j] = min(dp[j], dp[j-1]) + grid[i][j]。
  4. 4返回 dp[n-1]。
时间 O(m × n)空间 O(n)

易错点

  • 忘记更新 dp[0]:第一列的累计和还停留在第一行。
  • j 从右往左:dp[j-1] 还是上一行的值,左方来源用错。

代码与演示

主解法配有图解;对照解法用于比较思路、复杂度或实现方式。

演示输入grid = [[3,1,4,2],[2,5,1,3],[4,2,6,1]]

从 (0,0) 的 3 开始;第一行只能向右累加,第一列只能向下累加。

m × n
3 × 4
最小路径和 · 上方与左方取较小建立初值

dp[i][j]:从左上角走到 (i,j) 的最小路径和

i \ j01230123初dp[0][0]:3,当前目标4dp[0][1]:48dp[0][2]:810dp[0][3]:105dp[1][0]:5·dp[1][1]:尚未计算·dp[1][2]:尚未计算·dp[1][3]:尚未计算9dp[2][0]:9·dp[2][1]:尚未计算·dp[2][2]:尚未计算·dp[2][3]:尚未计算
dp[0][0]=3
未计算初值
1 / 8
查看 C++ 代码

延伸阅读

  • 和不同路径的区别只有转移里的运算符:路径数用加法,最小路径和先取 min 再加当前格。
  • 若格子允许负数,这个 DP 仍然成立;但“只能向右/向下”的无环性质是它成立的前提。