问题描述
问题分析
- 在三角形中选择一条自顶向下的路径,使得路径和最小。
- 最值问题是动态规划的经典题目,只要设计出递归关系,然后根据递归关系便能写出动态规划的形式。
- 首先要明确递归函数的意义:
getMinSum(List<List<Integer>> triangle, int i, int j) : 表示从triangle.get(i).get(j) 开始(包括),向下的最小路径和
- 递归关系为:
return triangle.get(i).get(j) + Math.min(getMinSum(triangle, i + 1, j), getMinSum(triangle, i + 1, j + 1))
- base case 便是 当
i == triangle.size()
便返回0
- 以上是自顶向下的思路,若自底向上看,那么便是动态规划的思路。
dp[i][j]
表示从triangle.get(i).get(j)
开始的最小路径和。- 依赖关系是
dp[i][j] = list.get(j) + Math.min(dp[i + 1][j], dp[i + 1][j + 1]);
- 初始条件是
i == size
时,那一行全是0,然后从下到上,从左到右依次填好这个二维表。
- 上述动态规划的思路用的空间复杂度为
O(N^2)
,但题目要要求是 O(N)
的空间复杂度,这就有牵扯出动态规划的空间优化问题,根据依赖关系可知,该行的元素只依赖于下一行的元素,严格来说,dp[i][j]
只依赖于 dp[i + 1][j]
与 dp[i + 1][j + 1]
,所以如果计算完第 i
行,那么 第 i+1
行便永远不会再用到,更严格来说,只要计算完 dp[i][j]
,dp[i + 1][j]
便永远不会用到,所以我们只用一行数组滚动更新即可,从下到上滚动,从左到右更新(可以防止覆盖)。这样,便只用了 O(N)
的时间复杂度完成了该问题。
经验教训
- 如何设计递归函数,明确递归函数的意义才是根本
- 对于动态规划,要明确
dp[i]
或者 dp[i][j]
的意思 - 动态规划的空间优化问题。
代码实现
public int minimumTotal(List<List<Integer>> triangle) {
if(triangle == null || triangle.size() == 0 || triangle.get(0) == null || triangle.get(0).size() == 0) {
return 0;
}
return getMinSum(triangle, 0, 0);
}
public int getMinSum(List<List<Integer>> triangle, int i, int j) {
if (i == triangle.size()) {
return 0;
}
return triangle.get(i).get(j) + Math.min(getMinSum(triangle, i + 1, j), getMinSum(triangle, i + 1, j + 1));
}
public int minimumTotal(List<List<Integer>> triangle) {
if(triangle == null || triangle.size() == 0 || triangle.get(0) == null || triangle.get(0).size() == 0) {
return 0;
}
int row = triangle.size() + 1;
int col = row;
int[][] dp = new int[row][col];
for (int j = 0; j < col; ++j) {
dp[row - 1][j] = 0;
}
for (int i = row - 2; i >= 0; --i) {
List<Integer> list = triangle.get(i);
for (int j = 0; j < list.size(); ++j) {
dp[i][j] = list.get(j) + Math.min(dp[i + 1][j], dp[i + 1][j + 1]);
}
}
return dp[0][0];
}
public int minimumTotal(List<List<Integer>> triangle) {
if(triangle == null || triangle.size() == 0 || triangle.get(0) == null || triangle.get(0).size() == 0) {
return 0;
}
int size = triangle.size();
int[] dp = new int[size + 1];
for (int i = size - 1; i >= 0; --i) {
List<Integer> list = triangle.get(i);
for (int j = 0; j < list.size(); ++j) {
dp[j] = list.get(j) + Math.min(dp[j], dp[j + 1]);
}
}
return dp[0];
}