传送门:https://leetcode-cn.com/problems/triangle/

题目

给定一个三角形 triangle ,找出自顶向下的最小路径和。
每一步只能移动到下一行中相邻的结点上。如(i,j) 可以移动到 (i+1,j)(i+1,j+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

解题思路:动态规划 -> 降维

如果将每一行的左端对齐,那么会形成一个等腰直角三角形,如下所示:

  1. [2]
  2. [3,4]
  3. [6,5,7]
  4. [4,1,8,3]

dp[i][j] 表示从三角形顶部 (0,0) 走到位置 (i,j) 的最小路径和。triangle[i][j] 表示 (i,j) 元素。
这里的位置 (i, j) 指的是三角形中第 i 行第 j 列(均从 0 开始编号)的位置。

  • 要想走到位置 (i,j),上一步就只能在位置 (i-1,j) 或者位置 (i-1,j-1) ,我们在这两个位置中选择一个路径和较小的来进行转移。
  • 当在 j=0 时,(i,0)只能从 (i-1,0) 过来,故 dp[i][0]=dp[i-1][0]
  • 当在 j= i_jlast 时,(i,i_jlast)只能从 (i-1,i-1_jlast) 过来

因此 dp[i][j]=dp[i-1][i-1_jlast]

状态转移方程:

[LeetCode]Dp120 三角形最小路径和 [ 漂亮在倒推 ] - 图1

复杂度分析

时间复杂度:[LeetCode]Dp120 三角形最小路径和 [ 漂亮在倒推 ] - 图2

空间复杂度:[LeetCode]Dp120 三角形最小路径和 [ 漂亮在倒推 ] - 图3,即为存储所有状态需要的空间。

代码

下图也是为啥需要分 3 类讨论的原因,因为有 3 种类型,比较繁琐。
若用反向只有一种情况
image.png

我的代码

  1. public class Demo {
  2. //动态规划,状态转移方程dp[i][j]=Min(dp[i-1][j]+nums[i][j],dp[i-1][j-1]+nums[i][j])
  3. public static int minimumTotal(List<List<Integer>> triangle) {
  4. if(triangle==null&&triangle.size()==0)return Integer.MIN_VALUE;
  5. if(triangle.size()==1)return triangle.get(0).get(0);
  6. int[][]dp=new int[triangle.size()][triangle.get(triangle.size()-1).size()];
  7. dp[0][0]=triangle.get(0).get(0);
  8. int res=Integer.MAX_VALUE;
  9. for (int i = 1; i < triangle.size(); i++) {
  10. for (int j = 0; j < triangle.get(i).size(); j++) {
  11. if(j==0)
  12. dp[i][j]=triangle.get(i).get(j)+dp[i-1][0];
  13. else if(j==(triangle.get(i).size()-1))
  14. dp[i][j]=triangle.get(i).get(j)+dp[i-1][j-1];
  15. else
  16. dp[i][j]=Math.min(dp[i-1][j-1]+triangle.get(i).get(j),dp[i-1][j]+triangle.get(i).get(j));
  17. if(i==triangle.size()-1)
  18. res=Math.min(dp[i][j],res);
  19. }
  20. }
  21. return res;
  22. }
  23. public static void main(String[] args) {
  24. List<List<Integer>>triangle=new ArrayList<List<Integer>>();
  25. //.....省略生成 triangle
  26. System.out.println(minimumTotal(triangle));
  27. }
  28. }

我的优化 -> 一维数组降维

当从下向上推时,只有一种情况,不需要分类讨论,且 dp[0][0] 就是答案,一维数组下就是dp[0]
image.png

  1. public class Demo {
  2. //动态规划,空间优化,必须从下向上
  3. public static int minimumTotal(List<List<Integer>> triangle) {
  4. if(triangle==null&&triangle.size()==0)return Integer.MIN_VALUE;
  5. if(triangle.size()==1)return triangle.get(0).get(0);
  6. int LENGTH=triangle.get(triangle.size()-1).size();
  7. int []dp=new int[LENGTH];//dp数组的长度为最后一行的元素个数
  8. int temp_x=0,temp_y=0;//temp_x保存会被覆盖的dp[j],temp_y保存 temp_x的右边值
  9. for (int i = triangle.size()-1; i >=0 ; i--) {
  10. if(i!=triangle.size()-1)
  11. temp_y=dp[triangle.get(i+1).size()-1];
  12. for (int j = triangle.get(i).size()-1; j >=0 ; j--) {
  13. if(i==triangle.size()-1) dp[j]=triangle.get(i).get(j);//dp初始化
  14. else{
  15. temp_x=dp[j];//修改前拿出来
  16. dp[j]=Math.min(dp[j]+triangle.get(i).get(j),temp_y+triangle.get(i).get(j));
  17. temp_y=temp_x;
  18. }
  19. }
  20. }
  21. return dp[0];
  22. }
  23. public static void main(String[] args) {
  24. List<List<Integer>>triangle=new ArrayList<List<Integer>>();
  25. //.....省略生成 triangle
  26. System.out.println(minimumTotal(triangle));
  27. }
  28. }

优化解释: