动态规划解决最小下降路径和问题
1. 题目链接
2. 题目描述
给定一个 n x n
的整数矩阵 matrix
,找到一条从第一行到最后一行的下降路径,使得路径上的数字和最小。下降路径可以从第一行的任意元素出发,每一步可以选择位于下一行正下方、左下方或右下方的元素。例如,位于 (i, j)
的元素可以下降到 (i+1, j-1)
、(i+1, j)
或 (i+1, j+1)
。
3. 示例分析
示例输入:
matrix = [[2,1,3],
[6,5,4],
[7,8,9]]
输出:13
解释:最小下降路径为 1 → 5 → 7
,路径和为 1 + 5 + 7 = 13
。
4. 算法思路
动态规划(Dynamic Programming)
使用动态规划解决该问题的核心思想是:定义 dp[i][j]
表示从第一行到达第 i
行第 j
列的最小路径和。为了处理边界条件(如矩阵边缘的列),我们扩展 dp
数组的边界,使其维度为 (n+1) x (n+2)
。
状态转移方程
对于每个位置 (i, j)
,其路径和的最小值由上一行的三个可能的位置决定:
dp[i][j] = matrix[i-1][j-1] + min(dp[i-1][j-1], dp[i-1][j], dp[i-1][j+1])
其中,matrix[i-1][j-1]
是当前位置的值,dp[i-1][...]
是上一行左、中、右三个位置的路径和的最小值。
初始化
dp[0][...] = 0
:第一行的所有位置初始化为0,表示从虚拟的第0行出发的路径和为0。- 其余位置初始化为
INT_MAX
,表示尚未计算。
5. 边界条件与注意事项
- 矩阵大小为1的情况:当
n=1
时,直接返回矩阵中唯一的元素。 - 索引转换:由于
dp
数组比原矩阵多一圈,访问matrix
时需要将索引调整为i-1
和j-1
。 - 处理边缘列:在矩阵的最左列(
j=1
)和最右列(j=n
),需要确保不会访问到无效的列(如j=0
或j=n+1
),此时这些位置的dp
值为INT_MAX
,不影响取最小值。
6. 代码实现
class Solution {
public:
int minFallingPathSum(vector<vector<int>>& matrix) {
int n = matrix.size();
vector<vector<int>> dp(n + 1, vector<int>(n + 2, INT_MAX));
// 初始化:从虚拟的第0行出发,路径和为0
for (int j = 0; j < n + 2; j++) dp[0][j] = 0;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
// 状态转移:取上一行左、中、右的最小值
dp[i][j] = matrix[i - 1][j - 1] +
min(min(dp[i - 1][j - 1], dp[i - 1][j]), dp[i - 1][j + 1]);
}
}
// 遍历最后一行,寻找最小值
int ret = INT_MAX;
for (int j = 0; j < n + 2; j++) {
ret = min(ret, dp[n][j]);
}
return ret;
}
};
代码解释
- 初始化
dp
数组:第0行初始化为0,其他位置为INT_MAX
。 - 填充
dp
数组:遍历每一行,根据上一行的三个相邻位置的最小值更新当前值。 - 获取结果:遍历最后一行的所有列,找到最小值作为最终结果。
通过这种动态规划的方式,时间复杂度为 O(n²)
,空间复杂度为 O(n²)
,能够高效解决该问题。