leetcode 120. Triangle(三角形)

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).

给出一个三角形,从顶端走向底端,经过路径的最短数字和是多少,每次只能走向相邻的数字。
原题是一个金字塔形的三角形,写成数组形式就变成了上述形状。

比如2可以走到3,4,3可以走向6,5
也就是说上一行j列到下一行可以走到j或者j+1
反之,j列的上一行可以是j或者j-1

思路:
每一行都求出上一行的数加下来的和,加的时候第j列的数选择上一行j和j-1中较小的相加
到最后一行求完后选择最小的那个值

(1) 二维DP

    public int minimumTotal(List<List<Integer>> triangle) {
        if(triangle == null || triangle.size() == 0) {
            return 0;
        }
        
        int n = triangle.size();
        //每行的元素数=行数,所以n行n列,补一行
        int[][] dp = new int[n+1][n+1];
        //后面用到取dp的min,所以先填上最大值
        for(int i = 0; i <= n; i++) {
            Arrays.fill(dp[i], Integer.MAX_VALUE);
        }
        
        for(int i = 1; i <= n; i++) {
            //第i行元素只到i
            for(int j = 1; j <= i; j++) {
                dp[i][j] = triangle.get(i-1).get(j-1);
                //第一行不需要和上一行相加,直接填入元素,不需要后续处理
                if(i == 1 && j == 1) {
                    continue;
                }
                //边界条件
                if(j == 1) {
                    dp[i][j] += dp[i-1][j];
                } else if(j == i) {
                    dp[i][j] += dp[i-1][j-1];
                } else {
                    dp[i][j] += Math.min(dp[i-1][j], dp[i-1][j-1]);
                }
            }
        }
        //array中的最大元素,java8
        return Arrays.stream(dp[n]).min().getAsInt();
        
    }

(2) 一维DP
因为每次只需要知道上一行的数,还需要保存当前行的数,因此只需要2行的dp数组

每次用dp[1][j]来求当前行的和,然后和dp的0行对换,作为下一次求dp[1]时的参考

c++中有简单的行对换的swap函数,还有求一行最小值的min_element函数
min_element返回iterator,所以用解引用操作符得到数值

    int minimumTotal(vector<vector<int>>& triangle) {
        int n = triangle.size();
        
        vector<vector<int>> dp(2, vector<int>(n, INT_MAX));
        
        for (int i = 0; i < n; i++) {
            for (int j = 0; j <= i; j++) {
                dp[1][j] = triangle[i][j];
                if (i == 0 && j == 0) {
                    continue;
                }
                
                if (j == 0) {
                    dp[1][j] += dp[0][j];
                } else if(j == i) {
                    dp[1][j] += dp[0][j - 1];
                } else {
                    dp[1][j] += min(dp[0][j - 1], dp[0][j]);
                }               
            }
            std::swap(dp[0], dp[1]);
        }
        
        return *std::min_element(dp[0].cbegin(), dp[0].cend());
    }

Java版

    public int minimumTotal(List<List<Integer>> triangle) {
        if(triangle == null || triangle.size() == 0) {
            return 0;
        }
        
        int n = triangle.size();
        //每行的元素数=行数,所以n行n列,补一行
        int[][] dp = new int[2][n];
        //后面用到取dp的min,所以先填上最大值
        for(int i = 0; i <= 1; i++) {
            Arrays.fill(dp[i], Integer.MAX_VALUE);
        }
        
        for(int i = 0; i < n; i++) {
            //第i行元素只到i
            for(int j = 0; j <= i; j++) {
                dp[1][j] = triangle.get(i).get(j);
                //第一行不需要和上一行相加,直接填入元素,不需要后续处理
                if(i == 0 && j == 0) {
                    continue;
                }
                //边界条件
                if(j == 0) {
                    dp[1][j] += dp[0][j];
                } else if(j == i) {
                    dp[1][j] += dp[0][j-1];
                } else {
                    dp[1][j] += Math.min(dp[0][j], dp[0][j-1]);
                }
            }
            swapArr(dp[0], dp[1]);
        }
        //array中的最大元素,java8
        //每次dp[1]中运算完之后要翻转到dp[0],因此最后看的是dp[0]
        return Arrays.stream(dp[0]).min().getAsInt();
        
    }
    
    public void swapArr(int[] arr1, int[] arr2) {
        //假设同一长度
        for(int i = 0; i < arr1.length; i++) {
            int tmp = arr1[i];
            arr1[i] = arr2[i];
            arr2[i] = tmp;
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蓝羽飞鸟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值