题目描述:给出一个数字三角形,求出从上到下的数字之和最小的路径。一个数字只能通往下一层与它相邻的两个数字。
解法一
这道题目的想法很简单:
- 从左到右,从上到下遍历矩阵,每次遍历到一个值,dp[i][j]表示从第一层到目前这个值(第i层第j个)的最小路径。
- 每次遍历,由于上一层的一个数字只能通往下一层与它相邻的两个数字,因此在更新dp数组时,只需考虑上一层的两个值即可:
dp[i][j] += min(dp[i-1][j-1], dp[i-1][j])
. - 要注意边界的情况。对每行的第一个元素,
dp[i-1][j-1]
不存在:dp[i][0] += dp[i-1][0]
。对每行的最后一个元素,dp[i-1][j]
不存在:dp[i][j] += dp[i-1][j-1]
。
代码如下:
class Solution {
public:
int min(vector<vector<int>>& triangle, int i, int j) {
if (triangle[i - 1].size() <= j)
return triangle[i - 1][j - 1];
return triangle[i - 1][j] < triangle[i - 1][j - 1] ?
triangle[i - 1][j] :
triangle[i - 1][j - 1];
}
int minimumTotal(vector<vector<int>>& triangle) {
int rows = triangle.size();
for (int i = 1; i < rows; ++i) {
triangle[i][0] += triangle[i - 1][0];
}
for (int i = 1; i < rows; ++i) {
for (int j = 1; j <= i; ++j) {
triangle[i][j] += min(triangle, i, j);
}
}
int res = 0xffffff;
for (int i = 0; i < rows; ++i) {
if (res > triangle[rows - 1][i])
res = triangle[rows - 1][i];
}
return res;
}
};
解法二
解法二本质上和解法一是一样的。不同之处在于,解法一的空间复杂度为O(n^2);而解法二的空间复杂度为O(n)。
用dp[j]
表示到某一层的第j个数字的最小路径的值。
实现方法为:将解法一的从左到右的循环改为从右到左。
在某一层的遍历开始前,dp数组保存着上一层的结果。每一层的值要在上一层的基础上获得,而在计算到triangle[i][j]
的最小路径时,dp[j],dp[j-1]
就是所需要的上一层的两个结果。从右向左求值,保证了每次计算的时候,dp[j],dp[j-1]
确实就是我们想要的值。
例如,计算这一层的dp[5]
时,要用到dp[5],dp[4]
,这时dp[5],dp[4]
还是上一层的值;计算结束后,dp[5]
更新为这一层的值,但是没有关系,因为接下来计算dp[4]
时,已经不需要用到dp[5]
的值了,所需要的dp[4],dp[3]
依旧是上一层的值。
边界的情况与解法一相同。
代码如下:
class Solution {
public:
int minimumTotal(vector<vector<int>>& triangle) {
int rows = triangle.size();
vector<int> dp(rows, INT_MAX);
dp[0] = triangle[0][0];
for (int i = 1; i < rows; ++i) {
for (int j = i; j > 0; --j) {
dp[j] = triangle[i][j] + min(dp[j], dp[j - 1]);
}
dp[0] += triangle[i][0];
}
int res = 0xffffff;
for (int i = 0; i < rows; ++i) {
if (res > dp[i])
res = dp[i];
}
return res;
}
};