120. 三角形最小路径和(贪心、分治、动态规划)


官方链接

https://leetcode-cn.com/problems/triangle

给定一个三角形,找出自顶向下的最小路径和。每一步只能移动到下一行中相邻的结点上。

例如,给定三角形:

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

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

说明:

如果你可以只使用 O(n) 的额外空间(n 为三角形的总行数)来解决这个问题,那么你的算法会很加分。

思路

  1. brute-force, 递归, n 层: left or right: O(2^n)
traverse(i, j) {
        traverse(i+1, j)
        traverse(i+1, j+1)
    }
  1. DP
  • a. 重复性(分治) problem(i, j) = min(sub(i+1, j) sub(i+1, j+1)) + a[i,j]
  • b. 定义状态数组: f[i,j]
  • c. DP 方程: f[i,j] = min(f[i+1, j], f[i+1, j+1]) + a[i,j]

参考链接

https://leetcode.com/problems/triangle/discuss/38735/Python-easy-to-understand-solutions-(top-down-bottom-up)

解法一

class Solution {
    public int minimumTotal(List<List<Integer>> triangle) {

        for (int i = triangle.size() - 2; i >= 0; i--) {
            for (int j = 0; j < triangle.get(i).size(); j++) {
                triangle.get(i).set(j, Math.min(triangle.get(i+1).get(j), triangle.get(i+1).get(j+1)) + triangle.get(i).get(j));
            }
        }
        return triangle.get(0).get(0);
    }
}
class Solution:
    def minimumTotal(self, triangle: List[List[int]]) -> int:
        '''
            dp[i][j] = triangle[i][j] + min(dp[i+1][j]+dp[i+1][j+1])
        '''
        mini,M = triangle[-1], len(triangle)
        for i in range(M-2, -1, -1):
            for j in range(len(triangle[i])):
                mini[j] = triangle[i][j] + min(mini[j], mini[j+1])

        return mini[0]
class Solution:
    def minimumTotal(self, triangle: List[List[int]]) -> int:
        '''
            dp[i][j] = triangle[i][j] + min(dp[i+1][j]+dp[i+1][j+1])
        '''
        dp = triangle
        M = len(triangle)
        for i in range(M-2, -1, -1):
            for j in range(len(triangle[i])):
                dp[i][j] += min(dp[i+1][j], dp[i+1][j+1])

        return dp[0][0]
class Solution:
    def minimumTotal(self, triangle: List[List[int]]) -> int:
        f = [0] * (len(triangle) + 1)
        for row in triangle[::-1]:
            for i in range(len(row)):
                f[i] = row[i] + min(f[i], f[i + 1])
        return f[0]
class Solution {
public:
    int minimumTotal(vector<vector<int>>& triangle) {
        for (int i = triangle.size() - 2; i >= 0; i--) {
            for (int j = 0; j < triangle[i].size(); j++) {
                triangle[i][j] += min(triangle[i+1][j], triangle[i+1][j+1]);
            }
        }
        return triangle[0][0];
    }
};

解法二

递归

class Solution {
    public int minimumTotal(List<List<Integer>> triangle) {

        int size = triangle.size();
        Integer[][] mem = new Integer[size][size];
        return helper(mem, 0, 0, triangle);
    }

    private int helper(Integer[][]mem, int level, int c, List<List<Integer>> triangle) {

        if(mem[level][c] != null) return mem[level][c];
        if (level == triangle.size() - 1) return triangle.get(level).get(c);
        int left = helper(mem, level + 1, c, triangle);
        int right = helper(mem,level + 1, c + 1, triangle);

        return mem[level][c] = Math.min(left, right) + triangle.get(level).get(c);
    }
}

解法二

dfs

class Solution {

    int minSum = 0;
    public int minimumTotal(List<List<Integer>> triangle) {
        if (triangle.size() == 0 || triangle.get(0).size() == 0) return 0;

        return dfs(triangle, 0, 0, "", 0);
    }

    private int dfs(List<List<Integer>> triangle, int i, int j, String path, int sum) {
        // terminator
        if (i == triangle.size() - 1) {
            path += triangle.get(i).get(j) + " # ";
            sum += triangle.get(i).get(j);
            System.out.println(path + " with sum: " + sum);
            return 0;
        }

        // process
        path += triangle.get(i).get(j) + " -> ";
        sum += triangle.get(i).get(j);

        // drill down
        dfs(triangle, i + 1, j, path, sum);
        dfs(triangle, i + 1, j + 1, path, sum);

        // clear
        // Note: no need to clear up the state `path`
        return 0;
    }
}