题目来源
题目描述
class Solution {
public:
/**
* @param triangle: a list of lists of integers
* @return: An integer, minimum path sum
*/
int minimumTotal(vector<vector<int>> &triangle) {
// write your code here
}
};
题目解析
分析:
- 首先明确“每一步可以移动到下面一行的相邻数字上。”是什么意思,即是说当前行的某列 j,可以选择移动到下一行的 j 或者 j+1
- “找到从顶部到底部的最小路径和”,可以将所有顶部到底部的路径和”求出来然后遍历找到
坐标型动态规划
(1)确定状态
- 最后一步:
- 无论用何种方式到达底部,总有最后一步。
- 设:m = triangle.size(),n = triangle[m - 1].size(),那么最后一步坐标可能是:
[m - 1, 0]
、[m - 1, 1]
、[m - 1, 2]
…[m - 1, n-1]
- 因为“每一步可以移动到下面一行的相邻数字上“,所以:
- 要走到
[m - 1, 0]
,前一步只可能是[m - 2, 0]
- 要走到
[m - 1, 1]
,前一步只可能是[m - 2, 0]
、[m - 2, 1]
- 要走到
[m - 1, 2]
,前一步只可能是[m - 2, 1]
、[m - 2, 2]
- …
- 要走到
[m - 1, n-3]
,前一步只可能是[m - 2, n -4]
、[m - 2, n - 3]
- 要走到
[m - 1, n-2]
,前一步只可能是[m - 2, n -3]
、[m - 2, n - 2]
- 要走到
[m - 1, n-1]
,前一步只可能是[m - 2, n - 2]
- 要走到
- 最优策略的从顶部到底部的最小路径和,那么:
- 先使得底部的每一个路径最小,即:
- 要
[0, 0]
走到[m - 1, 0]
最小,必须[0, 0]
到[m - 2, 0]
的路径最小 - 要
[0, 0]
走到[m - 1, 1]
最小,必须[0, 0]
到[m - 2, 0]
或者[m - 2, 1]
的路径最小的那一个 - …
- 要
- 遍历
[m - 1, 0]
~[m - 1, n-1]
,找到最小路径和
- 先使得底部的每一个路径最小,即:
- 子问题:
- 原问题:要求
[0,0]
到[i,j]
的最小路径和 - 子问题:先求
[0,0]
到[i-1,j-1]
的最小路径和以及[0,0]
到[i-1,j]
的最小路径和
- 原问题:要求
- 状态:
dp[i][j]
:表示从[0,0]
到[i,j]
的最小路径和
(2)状态转移方程:
- 由于每一步只能移动到下一行「相邻的节点」上,因此要想走到位置
[i, j]
,上一步就只能在位置[i - 1, j - 1]
或者位置[i - 1, j]
。我们在这两个位置中选择一个路径和较小的来进行转移,状态转移方程为:
d p [ i ] [ j ] = m i n ( d p [ i − 1 ] [ j − 1 ] , d p [ i − 1 ] [ j ] ) + A [ i ] [ j ] dp[i][j] = min(dp[i-1][j-1], dp[i - 1][j]) + A[i][j] dp[i][j]=min(dp[i−1][j−1],dp[i−1][j])+A[i][j] - 注意到,有j=0时,
d
p
[
i
−
1
]
[
j
−
1
]
dp[i-1][j-1]
dp[i−1][j−1]是没有意义的(从最后一步分析可以看出),此时状态转移方程为(即在第i行的最左侧时,我们只能从第
i
−
1
i-1
i−1行的最左侧转移过来):
d p [ i ] [ 0 ] = d p [ i − 1 ] [ 0 ] + A [ i ] [ 0 ] dp[i][0] = dp[i - 1][0]+ A[i][0] dp[i][0]=dp[i−1][0]+A[i][0] - 注意到,当位于第i行的最右侧时,第i-1行少了一个元素(第 i 行有 i+1个元素,它们对应的 j 的范围为 [0, i])。即当
j
=
i
j = i
j=i时,
d
p
[
i
−
1
]
[
j
]
dp[i-1][j]
dp[i−1][j]没有意义,因此状态转移方程为(即当我们在第 ii 行的最右侧时,我们只能从第 i-1i−1 行的最右侧移动过来):
d p [ i ] [ i ] = d p [ i − 1 ] [ i − 1 ] + A [ i ] [ i ] dp[i][i] = dp[i - 1][i - 1] + A[i][i] dp[i][i]=dp[i−1][i−1]+A[i][i]
(3)边界情况和初始条件
- 初始情况: d p [ 0 ] [ 0 ] = A [ 0 ] [ 0 ] dp[0][0] = A[0][0] dp[0][0]=A[0][0]
- 边界情况:无
(4)计算顺序
- 从上到下,从左到右
- dp[0][0]
- dp[1][0]、dp[1][1]
- dp[2][0]、dp[2][1]、dp[2][2]
- …
- dp[n-1][0]、dp[n-1][1]、dp[n-1][n-1]
- 最终的答案即为 d p [ n − 1 ] [ 0 ] dp[n-1][0] dp[n−1][0] 到 d p [ n − 1 ] [ n − 1 ] dp[n-1][n-1] dp[n−1][n−1]中的最小值,其中 n 是三角形的行数。
class Solution {
public:
/**
* @param triangle: a list of lists of integers
* @return: An integer, minimum path sum
*/
int minimumTotal(vector<vector<int>> &triangle) {
// write your code here
int n = triangle.size();
vector<vector<int>> f(n, vector<int>(n));
f[0][0] = triangle[0][0];
for (int i = 1; i < n; ++i) {
f[i][0] = f[i - 1][0] + triangle[i][0];
for (int j = 1; j < i; ++j) {
f[i][j] = min(f[i - 1][j - 1], f[i - 1][j]) + triangle[i][j];
}
f[i][i] = f[i - 1][i - 1] + triangle[i][i];
}
return *min_element(f[n - 1].begin(), f[n - 1].end());
}
};
当然,我们可以重用原数组
class Solution {
public:
/**
* @param triangle: a list of lists of integers
* @return: An integer, minimum path sum
*/
int minimumTotal(vector<vector<int>> &t) {
if(t.empty()){
return 0;
}
for (int i = 1; i < t.size(); ++i) {
for (int j = 0; j < t[i].size(); ++j) {
if(j == 0){
t[i][j] += t[i - 1][j];
}else if(i == j){
t[i][j] += t[i - 1][j - 1];
}else{
t[i][j] = std::min(t[i - 1][j - 1], t[i - 1][j]) + t[i][j];
}
}
}
int m = t.size();
return *min_element(t[m - 1].begin(), t[m - 1].end());
}
};