算法 LC 动态规划 - 三角形最小路径和

题目描述

给定一个三角形 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

题解

思路1: 动态规划
f[i][j] 表示从三角形顶部走到位置(i,j) 的最小路径和
f[i][j] = min(f[i−1][j−1],f[i−1][j])+c[i][j]

    static public func minimumTotal1(_ triangle: [[Int]]) -> Int {
        let n = triangle.count
        if n == 1 {return triangle[0][0]}
        var dp = Array(repeating: Array(repeating: Int.max/2, count: n), count: n)
        
        dp[0][0] = triangle[0][0]
        for i in 1..=1 {
                    dp[i][j] = min(dp[i][j],dp[i-1][j-1] + triangle[i][j])
                }
                dp[i][j] = min(dp[i][j],dp[i-1][j] + triangle[i][j])
            }
        }
        
        var ans = Int.max/2
        
        for i in 0..

优化
f[i][j] = min(f[i−1][j−1],f[i−1][j])+c[i][j] (0 f[i][0] = f[i-1][0] + c[i][0] (j=0)
f[i][i] = f[i-1][i-1] + c[i][i] (j=i)

    static public func minimumTotal2(_ triangle: [[Int]]) -> Int {
        let n = triangle.count
        if n == 1 {return triangle[0][0]}
        var dp = Array(repeating: Array(repeating: Int.max/2, count: n), count: n)
        
        dp[0][0] = triangle[0][0]
        for i in 1..

思路2:动态规划+空间优化

f[i][j] 只与f[i-1][...]有关,与f[i−2][..] 及之前的状态无关。
使用dp0表示上一层节点最小路径和,使用dp1表示当前层节点最小路径和,每次循环后,将dp1恢复原状

    static public func minimumTotal3(_ triangle: [[Int]]) -> Int {
        let n = triangle.count
        if n == 1 {return triangle[0][0]}
        var dp0 = Array(repeating: Int.max/2, count: n)
        var dp1 = Array(repeating: Int.max/2, count: n)
        
    
        dp0[0] = triangle[0][0]
        
        for i in 1..=1 {
                    dp1[j] = min(dp1[j], dp0[j-1]+triangle[i][j])
                }
                dp1[j] = min(dp1[j], dp0[j]+triangle[i][j])
            }
            dp0 = dp1
            dp1 = Array(repeating: Int.max/2, count: n)

        }
        
        var ans = Int.max/2
        
        for i in 0..

优化

f[i][j] 只与f[i-1][...]有关,与f[i−2][..] 及之前的状态无关
我们使用两个长度为n的一维数组进行转移,将i根据奇偶性映射到其中一个一维数组,那么i−1 就映射到了另一个一维数组。这样我们使用这两个一维数组,交替地进行状态转移

    static public func minimumTotal4(_ triangle: [[Int]]) -> Int {
        let n = triangle.count
        if n == 1 {return triangle[0][0]}
        var dp = Array(repeating: Array(repeating: Int.max/2, count: n), count: 2)
        
        dp[0][0] = triangle[0][0]
        for i in 1..

优化

从i到0递减地枚举j,只使用一个长度为n的一维数组f,完成状态转移

为什么只有在递减地枚举j时,才能省去一个一维数组?当我们在计算位置(i,j) 时,f[j+1] 到f[i] 已经是第i 行的值,而f[0] 到f[j] 仍然是第i−1 行的值。
此时我们直接通过f[j]=min(f[j−1],f[j])+c[i][j]进行转移,恰好就是在(i−1,j−1) 和(i−1,j) 中进行选择。
但如果我们递增地枚举j,那么在计算位置(i,j) 时,f[0] 到f[j−1] 已经是第i行的值。如果我们仍然使用上述状态转移方程,那么是在(i,j−1) 和(i−1,j) 中进行选择,就产生了错误

    static public func minimumTotal5(_ triangle: [[Int]]) -> Int {
        let n = triangle.count
        if n == 1 {return triangle[0][0]}
        var dp = Array(repeating:Int.max/2, count: n)
        
        dp[0] = triangle[0][0]
        for i in 1..

参考:https://leetcode-cn.com/problems/triangle
https://leetcode-cn.com/problems/triangle/solution/san-jiao-xing-zui-xiao-lu-jing-he-by-leetcode-solu/

你可能感兴趣的:(算法 LC 动态规划 - 三角形最小路径和)