「这是我参与2022首次更文挑战的第11天,活动详情查看:2022首次更文挑战」。
疫情有一些反复,街上大家又重新戴起了口罩。还是老老实实呆在家里刷leetcode,不给大家添乱,也尽快完成自己的新年小目标。
题目
给定一个三角形 triangle ,找出自顶向下的最小路径和。
每一步只能移动到下一行中相邻的结点上。相邻的结点 在这里指的是 下标 与 上一层结点下标 相同或者等于 上一层结点下标 + 1 的两个结点。也就是说,如果正位于当前行的下标 i ,那么下一步可以移动到下一行的下标 i 或 i + 1 。
示例 1:
输入:triangle = [[2],[3,4],[6,5,7],[4,1,8,3]]
输出:11
解释:如下面简图所示:
2
3 4
6 5 7
4 1 8 3
自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)。
示例 2:
输入:triangle = [[-10]]
输出:-10
思路
动态规划的基础题目,应该也是之前学习dp算法的时候,刷到的第一题。
可以按照三角形的形状,1层层计算下来,到达每个点的最小路径和,构建出1个代表最小路径和的相同形状三角形,然后就是求最下面1层的最小值。
如下图所示,第1层只有1个数值,就是2:
第2层,值是3和4,因为只能从第1层的唯一一个顶点下来,所以到达这2个点的最小路径和分别是3+2=5和4+2=6:
第3层,6这个点只能从第2层左边这个点下来,所以这个点的最小路径和是6+5=11,中间这个点,可以则第2层的左右2个点,这里选取较小的左边的点,所以这个点的最小路径和是5+5=10,7这个点,只能选择第2层右边的点下来,所以这个点的最小路径和是7+6=13:
第4层,跟上面的计算方法一样,4点的最小路径和分别为 4+11=15,1+10=11,8+10=18,3+13=16:
所以,整个三角形的最小路径和 = min(15,11,18,16) = 11。
优化
我们可以根据上述的思路去编码,但是我们注意到,求解每1层的最小和的时候,只跟上1层的最小和和当前层的值有关,所以,我们本来要开辟一个跟原三角形一样大的空间n*n/2,现在可以修改成只开辟2行,1行用于存储上一行的最小路径和,1行用于存储当前行的最小路径和,2行交替使用,这样降低了一些空间复杂度。
另外,我们注意到,由于每1行都会比上1行多一个元素,而f(n)是跟f(n-1)和f(n)相关,如果从右往左计算,刚好覆盖掉的fn不会再被前面的元素计算使用到,所以,这样就只需要1行来存储即可。
Java版本代码
class Solution {
public int minimumTotal(List<List<Integer>> triangle) {
int len = triangle.size();
int[] dp = new int[len];
dp[0] = triangle.get(0).get(0);
for (int i = 1; i < len; i++) {
// 先处理好尾,只有1种可能
dp[i] = dp[i-1] + triangle.get(i).get(i);
// 中间的元素有2种可能,需要取最小值
for (int j = i-1; j > 0; j--) {
dp[j] = Integer.min(dp[j-1], dp[j]) + triangle.get(i).get(j);
}
// 再处理好首,也只有1种可能
dp[0] += triangle.get(i).get(0);
}
int ans = dp[0];
for (int i = 1; i < len; i++) {
ans = Integer.min(ans, dp[i]);
}
return ans;
}
}