64. 最小路径和 
题目描述
给定一个包含非负整数的 m × n 网格 grid,从左上角出发,每次只能向右或向下移动一步,求到达右下角时经过数字之和最小的路径。
路径和需要包含起点和终点的数字。
示例:
1. 为什么使用动态规划
对于任意格子 (i, j),因为只能向右或向下移动,所以到达它的最后一步只有两种来源:
- 从上方
(i - 1, j)向下走。 - 从左侧
(i, j - 1)向右走。
如果已经知道到达上方和左侧的最小路径和,那么只需选择较小者,再加上当前格子的值,就能得到到达当前格子的最小路径和。
问题具有:
- 最优子结构:当前最优解可以由前驱位置的最优解推出。
- 重复子问题:递归搜索时,同一个格子的最小路径和会被多次计算。
因此适合使用动态规划。
2. 状态定义
定义:
根据这个定义,最终答案是:
状态定义必须明确包含当前格子的值,否则初始化和转移时很容易重复加或漏加 grid[i][j]。
3. 状态转移方程
对于内部格子 (i, j),上方和左侧都存在:
这里取 min,因为题目要求路径和最小;最后再加上进入当前格子必须付出的 grid[i][j]。
这两个来源覆盖了到达当前格子的所有合法路径,而且互不遗漏。
4. 初始化
起点
起点没有前驱位置,路径和就是它自身:
第一列
第一列中的格子只能从上方到达,因此需要逐行累计:
第一行
第一行中的格子只能从左侧到达,因此需要逐列累计:
第一行和第一列不能像「不同路径」那样全部初始化为 1。本题保存的是路径和,而不是路径数量,边界状态必须累计沿途经过的网格值。
5. 遍历顺序
计算 dp[i][j] 时需要:
因此应按从上到下、从左到右的顺序遍历:
6. 示例推演
对于:
初始化起点、第一行和第一列:
继续计算内部格子:
最终 DP 表:
右下角的 dp[2][2] = 7,所以最小路径和为 7。
注意,dp[i][j] 只保存最小路径和,并没有保存具体路径。示例中的最优路径可以通过从终点反向比较前驱状态恢复。
7. 二维动态规划实现
8. 使用哨兵统一边界处理
也可以额外创建一行一列,把越界位置初始化为 Infinity:
这里实际网格 (0, 0) 对应 DP 表中的 (1, 1)。人工设置:
可以让起点通过统一公式得到:
越界位置必须使用 Infinity,不能使用 0。如果使用 0,Math.min 会把不存在的越界路径误认为更优路径,导致第一行和第一列无法正确累计。
这与「不同路径」中的 dp[0][1] = 1 思路相同:哨兵值由转移运算的单位元决定。
- 路径计数使用加法,入口值设为
1。 - 最小路径和使用取最小值再加权,入口值设为
0,其他非法来源设为Infinity。
9. 一维数组空间优化
当前行只依赖上一行和当前行左侧,因此可以使用一维数组。
更新 dp[j] 前:
更新 dp[j - 1] 后:
代码如下:
遍历必须从左向右进行,确保 dp[j - 1] 已经表示当前行,而 dp[j] 在更新前仍表示上一行。
10. 原地动态规划
如果允许修改输入,可以直接把 grid 当作 DP 表:
这种写法的额外空间复杂度是 O(1),但会覆盖原始网格。面试时应主动说明这个副作用;如果调用方仍需要原始输入,应使用独立 DP 数组。
11. 正确性说明
可以按照遍历顺序进行归纳证明:
- 起点
dp[0][0] = grid[0][0]显然正确。 - 第一行和第一列都只有唯一走法,累计沿途网格值即可得到最小路径和。
- 假设当前格子的上方和左侧状态都已经正确。任何到达当前格子的合法路径,最后一步必然来自这两个位置之一。选择两者中较小的路径和,再加当前格子的值,就得到所有合法路径中的最小值。
因此每个 dp[i][j] 都符合状态定义,最终的 dp[m - 1][n - 1] 就是答案。
12. 复杂度分析
一维 DP 还可以选择较短的维度作为数组长度,把空间复杂度进一步写为 O(min(m, n)),但需要相应调整遍历方向和网格访问方式。
13. 边界条件与易错点
边界条件:
- 只有一个格子时,答案就是
grid[0][0]。 - 只有一行时,只能一直向右,答案是该行所有元素之和。
- 只有一列时,只能一直向下,答案是该列所有元素之和。
- 网格元素可以为
0,不能用“值是否为 0”判断状态是否已经计算。
易错点:
- 路径和必须包含起点与终点。
- 第一行和第一列只有一个前驱,不能直接套用有两个前驱的转移式而不处理越界。
- 不合法的前驱在最小值问题中应视为
Infinity,不能设为0。 - 一维压缩必须从左向右更新,否则会破坏状态依赖关系。
- 原地 DP 会修改输入,使用前要确认题目或调用方是否允许。
- 本题只能向右或向下,不能从右侧或下方转移过来。
14. 面试追问
为什么本题不能使用贪心,每次走向数值更小的相邻格子?
当前较小的格子不代表后续路径和更小。局部选择可能进入一片代价很高的区域,而暂时较大的格子后面可能连接大量 0。动态规划比较的是到达每个位置的完整累计代价,能够保留全局最优性。
如果需要返回具体路径怎么办?
可以在计算 DP 时额外记录每个格子的最优前驱,最后从右下角反向回溯到左上角;也可以保留完整 DP 表,从终点开始每次走向上方和左侧中 DP 值较小的位置。若两者相等,则可能存在多条最优路径。
如果网格中存在障碍物怎么办?
把障碍格视为不可达状态,其 DP 值设为 Infinity,其他格子继续从可达的上方和左侧取最小值。如果终点最终仍为 Infinity,说明不存在合法路径。
如果允许向四个方向移动怎么办?
此时状态依赖可能形成环,不能再简单地按行列顺序做一次 DP。如果边权非负,可以把格子看作图节点,使用 Dijkstra 算法求最短路径。
15. 可迁移总结
这类网格最值问题可以沿着下面的思路分析:
本题最值得迁移的两个细节是:
- 初始化必须严格服从状态定义,边界格子只有一个合法来源。
- 最小值问题中的不可达状态应使用
Infinity,避免非法来源被误选为更优解。
掌握这套模型后,可以继续处理带障碍网格、三角形最小路径和、下降路径最小和等问题。

