一、题目
给定一个三角形,找出自顶向下的最小路径和。每一步只能移动到下一行中相邻的结点上。
例如,给定三角形:
[
[2],
[3,4],
[6,5,7],
[4,1,8,3]
]
自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)。
说明:
如果你可以只使用 O(n) 的额外空间(n 为三角形的总行数)来解决这个问题,那么你的算法会很加分。
二、思路——动态规划
我们的目标是找出自顶向下的最小路径和。先分析两种简单的情况:
-
如果三角形只有两层,如
[2], [3,4]
我们找到最后一层的最小值,再加上第一层的数,就是最小路径和,即 2 + 3 = 5。
-
如果三角形有三层,如
[2], [3,4], [6,5,7]
我们先找左下角 3、6、5 的最小路径和 3 + 5 = 8,再找右下角 4、5、7 的最小路径和 4 + 5 = 9,两者的最小值再加上第一层的数就是整个三角形的最小路径和,即 2 + 8 = 10。
可以看出,分析三层三角形的过程,是从下往上进行计算的,我们也可以用这个思路分析层数更多的三角形。也就是,从最底下的一层逐层向上计算,计算至顶层时,就得到了整个三角形的最小路径和。
因此,我们定义状态:
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j] 表示“从三角形的最底层到当前位置的最小路径和”。由于每一步只能移动到下一行中相邻的节点上,所以,当前位置的最小路径和就是它的下一行的两个相邻节点最小路径和的最小值加上当前位置的数字,可得到递推方程:
d
p
[
i
]
[
j
]
=
M
a
t
h
.
m
i
n
(
d
p
[
i
+
1
]
[
j
]
,
d
p
[
i
+
1
]
[
j
+
1
]
)
+
t
r
i
a
n
g
l
e
[
i
]
[
j
]
dp[i][j] = Math.min(dp[i + 1][j], dp[i + 1][j + 1]) + triangle[i][j]
dp[i][j]=Math.min(dp[i+1][j],dp[i+1][j+1])+triangle[i][j] 数组初始化: 我们采用自下而上的策略,首先考虑的是最底下一行的元素,最底下一行的元素就表示它们到倒数第二行元素的路径。因此直接将最后一行的元素填入状态数组中,然后从下往上计算。
三、代码
class Solution {
public int minimumTotal(List<List<Integer>> triangle) {
int n = triangle.size();
int[][] dp = new int[n][n];
// 数组初始化:三角形最后一行的元素填入数组
List<Integer> lastRow = triangle.get(n - 1);
for(int i = 0; i < n; i++){
dp[n - 1][i] = lastRow.get(i);
}
// 自下而上计算
for(int i = n - 2; i >= 0; i--){
List<Integer> curRow = triangle.get(i);
for(int j = 0; j < i + 1; j++){
dp[i][j] = Math.min(dp[i + 1][j], dp[i + 1][j +1]) + curRow.get(j);
}
}
return dp[0][0];
}
}