LeetCode120. 三角形最小路径和

博客围绕LeetCode120三角形最小路径和问题展开,给定三角形,需找出自顶向下最小路径和,每步只能移到下一行相邻结点。介绍了集合、状态表示、属性及状态转移,还给出代码及优化方案。

LeetCode120. 三角形最小路径和

给定一个三角形,找出自顶向下的最小路径和。每一步只能移动到下一行中相邻的结点上。相邻的结点 在这里指的是 下标与上一层结点下标 相同或者等于 上一层结点下标 + 1 的两个结点。

例如,给定三角形:

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

自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)。

集合:从起点到终点的所有路径组成的集合

状态表示:dp[i][j],表示从起点到第i层第j列的所有路径组成的集合中,路径值最小的

属性:计算出 从起点到第i层第j列的所有路径组成的集合中,路径值最小的

状态转移:
f(i,j)={f(0,0)i=0,j=0f(i−1,0)+nums[i][j]0<i≤n−1,j=0min(f(i−1,j−1),f(i−1,j))+nums[i][j]0<i≤n−1,0<j≤if(i−i,i−1)+nums[i][i]i=n−1 f(i,j)=\begin{cases} f(0,0) \quad i=0,j=0 \\ f(i-1,0) + nums[i][j] \quad 0<i\leq n-1,j=0 \\ min(f(i-1, j-1), f(i-1,j)) + nums[i][j] \quad 0<i \leq n-1, 0<j \leq i \\ f(i-i, i-1)+nums[i][i] \quad i=n-1 \end{cases} f(i,j)=f(0,0)i=0,j=0f(i1,0)+nums[i][j]0<in1,j=0min(f(i1,j1),f(i1,j))+nums[i][j]0<in1,0<jif(ii,i1)+nums[i][i]i=n1

代码一:

class Solution {
    public int minimumTotal(List<List<Integer>> triangle) {
        int n = triangle.size();
        int[][] dp = new int[n][n];
        dp[0][0] = triangle.get(0).get(0);
        for (int i = 1; i < n; i++) {
            for (int j = 0; j <= i; j++) {
                dp[i][j] = Integer.MAX_VALUE;
                // 只有当j大于0时(不在第一列),才能从左上角下来
                // 此判断包含了j等于i(在最后一列)
                if (j > 0) {
                    dp[i][j] = Math.min(dp[i][j], dp[i - 1][j - 1] + triangle.get(i).get(j));
                }
                // 只有当j小于i时(不在最后一列),才能从正上方下来
                // 此判断包含了j等于0,(在第一列)
                if (j < i) {
                    dp[i][j] = Math.min(dp[i][j], dp[i - 1][j] + triangle.get(i).get(j));
                }
            }
        }
        int result = dp[n - 1][0];
        for (int i = 1; i < n; i++) {
            result = Math.min(result, dp[n - 1][i]);
        }
        return result;
    }
}

代码二:

class Solution {
    public int minimumTotal(List<List<Integer>> triangle) {
        int n = triangle.size();
        // 开辟一个dp数组,保存中间计算结果
        // dp[i][j],表示从起点走到第i行第j列的左右路径中,最小的路径值
        // dp[i][j] = min(dp[i-1][j-1], dp[i-1][j]) + triangle[i][j]
        // 当j等于0时(第一列),只能从dp[i-1][0]下来
        // 当j等于i时(最后一列),只能dp[i-1][i-1]下来(最后一列)
        int[][] dp = new int[n][n];
        dp[0][0] = triangle.get(0).get(0);
        for (int i = 1; i < n; i++) {
            // 第一列只能从上一行的第一列到达
            dp[i][0] = dp[i - 1][0] + triangle.get(i).get(0);
            // 第一列到倒数第二列
            for (int j = 1; j < i; j++) {
                dp[i][j] = Math.min(dp[i - 1][j - 1], dp[i - 1][j]) + triangle.get(i).get(j);
            }
            // 最后一列只能从上一层的最后一列到达
            dp[i][i] = dp[i - 1][i - 1] + triangle.get(i).get(i);
        }
        // 遍历最后一层,找到最小值
        int result = dp[n - 1][0];
        // triangle的最后一行的元素个数等于triangle的行数
        // 所以这里可以从i循环到n
        for (int i = 1; i < n; i++) {
            result = Math.min(result, dp[n - 1][i]);
        }
        // 返回最小值
        return result;
    }
}

优化一:

class Solution {
    public int minimumTotal(List<List<Integer>> triangle) {
        int n = triangle.size();
        // 使用滚动数组,来记录数据,因为遍历到第i行时,只用到了第i-1行的数据,i-1之前的数据都已经不会再被使用
        int[][] dp = new int[2][n];
        dp[0][0] = triangle.get(0).get(0);
        for (int i = 1; i < n; i++) {
            for (int j = 0; j <= i; j++) {
                //(& 1)等价于 %2
                dp[i & 1][j] = Integer.MAX_VALUE;
                if (j > 0) {
                    dp[i & 1][j] = Math.min(dp[i & 1][j], dp[i - 1 & 1][j - 1] + triangle.get(i).get(j));
                }
                if (j < i) {
                    dp[i & 1][j] = Math.min(dp[i & 1][j], dp[i - 1 & 1][j] + triangle.get(i).get(j));
                }
            }
        }
        int result = dp[n - 1 & 1][0];
        for (int i = 1; i < n; i++) {
            result = Math.min(result, dp[n - 1 & 1][i]);
        }
        return result;
    }
}

优化二:

class Solution {
    public int minimumTotal(List<List<Integer>> triangle) {
        int n = triangle.size();
        // 开辟一个dp数组,保存中间计算结果
        // dp[i][j],表示从起点走到第i行第j列的左右路径中,最小的路径值
        // dp[i][j] = min(dp[i-1][j-1], dp[i-1][j]) + triangle[i][j]
        // 当j等于0时(第一列),只能从dp[i-1][0]下来
        // 当j等于i时(最后一列),只能dp[i-1][i-1]下来(最后一列)
        int[][] dp = new int[2][n];
        dp[0][0] = triangle.get(0).get(0);
        for (int i = 1; i < n; i++) {
            // 第一列只能从上一行的第一列到达
            dp[i & 1][0] = dp[i - 1 & 1][0] + triangle.get(i).get(0);
            // 第一列到倒数第二列
            for (int j = 1; j < i; j++) {
                dp[i & 1][j] = Math.min(dp[i - 1 & 1][j - 1], dp[i - 1 & 1][j]) + triangle.get(i).get(j);
            }
            // 最后一列只能从上一层的最后一列到达
            dp[i & 1][i] = dp[i - 1 & 1][i - 1] + triangle.get(i).get(i);
        }
        // 遍历最后一层,找到最小值
        int result = dp[n - 1 & 1][0];
        // triangle的最后一行的元素个数等于triangle的行数
        // 所以这里可以从i循环到n
        for (int i = 1; i < n; i++) {
            result = Math.min(result, dp[n - 1 & 1][i]);
        }
        // 返回最小值
        return result;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值