题目
一个正立三角形,上层的点只能接触下层与其相邻的点,求一条从顶层到底层的路径,使路径上所有点之和最小。
例子:
[
[2],
[3,4],
[6,5,7],
[4,1,8,3]
]
上面这三角形自顶向下的最小路径和为 11 ( 2 + 3 + 5 + 1 = 11)
思路
采用动态规划,但是有两种不同方式
自顶向下
这是最常规想法,从根往下遍历,计算出到达每层中各个点的最小路径,最后从到达最底层的各条路径选出最小值即可
状态:dp[i, k]表示从最顶层到第 i 层中第k个位置的路径的最小和
状态转移方程:
若k = 0, dp[i+1, k] = dp[i, k] + triangle[i+1, k]
若k > 0, dp[i+1, k] = min{dp[i, k-1], dp[i, k]} + triangle[i+1, k]
若k = i+1, dp[i+1, k] = dp[i, k-1] + triangle[i+1, k]自底向上
这个比自顶向下更方便,状态方程只需一个
状态:dp[i, k]表示从最底层到第 i 层中第k个位置的路径的最小和
状态转移方程:dp[i-1, k] = min{dp[i, k], dp[i, k+1]} + triangle[i-1, k]
改进:为了使空间降到O(n),用一维数组 dp[i] 代替二维数组 dp[k, i],滚动计算
注意同一层里dp[i]的计算顺序:
- 当自顶向下计算时,每层必须从右往左算dp[i]
- 当自底向上时,每层必须从左往右算dp[i]
因为新值会覆盖旧值,不按上面顺序算会导致新生成的dp[i]影响同一层里后续dp[k]的计算而出错
代码
自顶向下
class Solution {
public:
int minimumTotal(vector<vector<int>>& triangle) {
int *dp = new int[triangle.size()];
dp[0] = triangle[0][0];
for (int row = 1; row < triangle.size(); row++) {
//每层必须从右往左计算dp[i]
for (int col = row; col >= 0; col--) {
if (col == 0) dp[col] = dp[col] + triangle[row][col];
else if (col == row) dp[col] = dp[col - 1] + triangle[row][col];
else dp[col] = min(dp[col - 1], dp[col]) + triangle[row][col];
}
}
//取出通往最底层的最小路径和
int minPath = dp[0];
for (int col = 0; col < triangle.size(); col++) minPath = dp[col] < minPath? dp[col]: minPath;
return minPath;
}
};
自底向上
class Solution {
public:
int minimumTotal(vector<vector<int>>& triangle) {
//自底向上,初始路径数目
int path_num = triangle.size();
//重复利用dp一维空间
int *dp = new int[path_num];
for(int i = 0; i < path_num; i++) dp[i] = triangle[path_num - 1][i];
for(int row = path_num - 2; row >= 0; row--) {
//注意每层从左往右算,不然会新值会覆盖旧值而出错
for (int col = 0; col <= row; col++) {
dp[col] = min(dp[col], dp[col + 1]) + triangle[row][col];
}
}
return dp[0];
}
};