Given a triangle, find the minimum path sum from top to bottom. Each step you may move to adjacent numbers on the row below.
For example, given the following triangle
[ [2], [3,4], [6,5,7], [4,1,8,3] ]
The minimum path sum from top to bottom is 11
(i.e., 2 + 3 + 5 + 1 = 11).
Note:
Bonus point if you are able to do this using only O(n) extra space, where n is the total number of rows in the triangle.
题意:给定一个三角阵,三角阵里每个位置有一个数值。
找到一条由顶到底的路径,使得路径里的数值总和最小。
每次可以向当前位置的下一行的左或右走。
思路1:dfs + 备忘录
在第i行的第j个位置,往下有两种走法的选择,往第i +1行的第j个位置或第j+1位置
在第i+1行的第j个位置或第j+1个位置的子问题与原问题相同,可用dfs递归求解
用一个数组_min[i][j]保存求解过程的值,避免重复计算。
复杂度:时间O(n ^2),空间O(n^2)
思路2:dp + 滚动数组
设dp[i][j] 表示到达第i行第j个位置的路径的最小数值,则状态转移方程为
dp[i][j] = min(dp[i-1][k]) + A[i][j] 其中 dp[i-1][k]表示可到达dp[i][j]状态的上一个状态
在这里 k可以取 j -1 或 j,A[i][j]表示这个位置的值
因为在第i行只需要用到第i-1行的数据所以可以不用保存i-1前面的最优值。
最后只要用到数组 dp[j] 就可以
复杂度:时间O(n^2),空间O(n)
//思路1
vector<vector<int> > _min, _triangle;
int dfs(int i, int j){
if(i == _triangle.size() - 1){
return _triangle[i][j];
}
if(j < 0 || j >= _triangle[i].size()) return INT_MAX;
if(_min[i][j] != INT_MAX) return _min[i][j]; //某个特殊值表示 dfs(i,j)还没被计算过
return _min[i][j] = min(dfs(i + 1, j), dfs(i + 1, j + 1)) + _triangle[i][j];
}
int minimumTotal(vector<vector<int> > &triangle) {
if(triangle.empty()) return 0;
_triangle = triangle;
_min = vector<vector<int> >(triangle.size(), vector<int>(triangle[triangle.size() - 1].size(), INT_MAX));
return dfs(0, 0);
}
//思路2
int minimumTotal(vector<vector<int> > &triangle) {
if(triangle.empty()) return 0;
vector<int> dp(triangle.back().size(), 0);
//初始化
int i = 0;
for_each(triangle[0].begin(), triangle[0].end(),[&i, &dp](int &v){
dp[i++] = v;
});
//迭代更新dp
for(i = 1; i < triangle.size(); ++i){
int dp_j_1 = dp[0];
for(int j = 0; j < triangle[i].size(); ++j){
int tem = dp[j];
if(j == triangle[i].size() - 1) dp[j] = dp_j_1;
dp[j] = min(dp_j_1 ,dp[j]) + triangle[i][j];
dp_j_1 = tem;
}
}
int _min = INT_MAX;
for(int j = 0; j < dp.size(); ++j)
_min = min(_min, dp[j]);
return _min;
}