给定一个包含非负整数的 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
核心是从网格左上角走到右下角(只能向右 / 向下走),求路径上数字之和的最小值 ,考虑动态规划算法。
动态规划算法的两个关键特征:
- 最优子结构:到达位置
(i,j)的最小路径和,只依赖于「到达上方(i-1,j)的最小路径和」和「到达左方(i,j-1)的最小路径和」(因为只能向右 / 向下走,没有其他来源); - 无后效性:一旦算出
(i,j)的最小路径和,后续计算不会改变这个值(路径选择不回头)。
动态规划的核心思路(四步走)
步骤 1:定义状态(最关键的一步)
设 dp[i][j] 表示「从左上角 (0,0) 走到 (i,j) 的最小路径和」。目标就是求 dp[m-1][n-1](m 是行数,n 是列数)。
步骤 2:推导状态转移方程
因为只能从「上方 (i-1,j)」或「左方 (i,j-1)」走到 (i,j),所以:dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + grid[i][j](走到 (i,j) 的最小和 = 上 / 左中更小的那个 + 当前格子的数值)
步骤 3:处理边界条件
- 第一行(
i=0):只能从左边走过来,所以dp[0][j] = dp[0][j-1] + grid[0][j]; - 第一列(
j=0):只能从上面走过来,所以dp[i][0] = dp[i-1][0] + grid[i][0]; - 起点(
0,0):dp[0][0] = grid[0][0](只有自己)。
步骤 4:确定遍历顺序
因为 dp[i][j] 依赖左 / 上的结果,所以按「从上到下、从左到右」遍历网格即可。
代码实现:
1.基础 DP(易理解,用二维数组)
class Solution {
public:
int minPathSum(vector<vector<int>>& grid) {
if (grid.empty() || grid[0].empty()) return 0;
int m = grid.size(), n = grid[0].size();
// 定义dp数组,和网格同大小
vector<vector<int>> dp(m, vector<int>(n, 0));
// 初始化起点
dp[0][0] = grid[0][0];
// 初始化第一列(只能从上到下)
for (int i = 1; i < m; ++i) {
dp[i][0] = dp[i-1][0] + grid[i][0];
}
// 初始化第一行(只能从左到右)
for (int j = 1; j < n; ++j) {
dp[0][j] = dp[0][j-1] + grid[0][j];
}
// 遍历网格,填充dp数组
for (int i = 1; i < m; ++i) {
for (int j = 1; j < n; ++j) {
dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + grid[i][j];
}
}
return dp[m-1][n-1];
}
};
2.空间优化(进阶,O (1) 额外空间)
观察发现:dp[i][j] 只依赖左 / 上的值,且可以直接在原网格上修改(覆盖),无需额外 dp 数组:
class Solution {
public:
int minPathSum(vector<vector<int>>& grid) {
if (grid.empty() || grid[0].empty()) return 0;
int m = grid.size(), n = grid[0].size();
// 初始化第一列
for (int i = 1; i < m; ++i) {
grid[i][0] += grid[i-1][0];
}
// 初始化第一行
for (int j = 1; j < n; ++j) {
grid[0][j] += grid[0][j-1];
}
// 直接在原数组上计算
for (int i = 1; i < m; ++i) {
for (int j = 1; j < n; ++j) {
grid[i][j] += min(grid[i-1][j], grid[i][j-1]);
}
}
return grid[m-1][n-1];
}
};
以后遇到类似的「网格路径 + 最值」问题,按这个流程想:
- 看移动规则:能走哪些方向(本题只有右 / 下)→ 确定状态转移的来源;
- 定义状态:
dp[i][j]表示 “到(i,j)的目标值(和 / 步数 / 数量)”; - 推转移方程:从来源中选最优(min/max)+ 当前值;
- 处理边界:第一行 / 第一列(只能单方向走)。