博文中会简要介绍Leetcode P0120题目分析及解题思路。
“Triangle”是一道比较基础一些的动态规划问题。这道题可以使用递归回溯的做法来完成,但由于需要函数栈,所以会有额外的空间开销。如果想要在O(n)的空间复杂度内完成,使用bottom-up
的动态规划方法更加容易,并且每个数仅会访问一次,时间复杂度也更高。
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.
控制空间复杂度的关键是控制动态规划的维度,这道题看似是二维的动态规划,需要O(n^2)的空间开销来存放数组,但由于每一层都会比下一层少一个元素,实际上只需要O(n)的空间来存储最下层的所有元素,然后后续向上的每层元素都存储在相同的数组中,上一层覆盖下一层即可。
这道题的递推表达式比较简答,具体如下:
令dp[i]是以第r层第i列的结点为起点到叶子结点的路径中的最短路径的值,
for r: row-1 -> 1;
dp[i] = min(dp[i], dp[i+1])+triangle[r-1][i]
end
每一层的结点都等于其下一层的左右结点的最短路径中的最小值再加上自身的权重。
以下是Java的题解代码实现。
class Solution {
public int minimumTotal(List<List<Integer>> triangle) {
int rows = triangle.size();
if (rows <= 1)
return triangle.get(rows-1).get(0);
int[] dp = new int[rows];
for (int idx=0; idx<triangle.get(rows-1).size(); ++idx)
dp[idx] = triangle.get(rows-1).get(idx);
// DP
for (int r=rows-2; r>=0; --r) {
for (int c=0; c<=r; ++c) {
dp[c] = Math.min(dp[c], dp[c+1])+triangle.get(r).get(c);
}
}
return dp[0];
}
}
以下是C++的题解代码实现。
class Solution {
public:
int minimumTotal(vector<vector<int>>& triangle) {
int rows = triangle.size();
vector<int> dp;
for (int num: triangle[rows-1])
dp.push_back(num);
// DP
for (int r=rows-2; r>=0; --r) {
for (int c=0; c<=r; ++c) {
dp[c] = min(dp[c], dp[c+1])+triangle[r][c];
}
}
return dp[0];
}
};