算法 - 动规02(Swift版本)

83 阅读3分钟

题目1:62.不同路径

讲解
leetcode

// 自己写的,初始化的操作不得当,导致代码逻辑处理的复杂度变高了。
class Solution {
    func uniquePaths(_ m: Int, _ n: Int) -> Int {
        var dp = Array(repeating: Array(repeating: 0, count: n), count: m)
        dp[0][0] = 1
        for i in 0..<m {
            for j in 0..<n {
                if i == 0, j == 0 { continue }
                if i > 0, j > 0 {
                    dp[i][j] = dp[i][j - 1] + dp[i - 1][j]
                } else if i == 0 {
                    dp[i][j] = dp[i][j - 1]
                } else {
                    dp[i][j] = dp[i - 1][j]
                }
            }
        }
        return dp[m - 1][n - 1]
    }
}

// 初始化优化后
class Solution {
    func uniquePaths(_ m: Int, _ n: Int) -> Int {
        var dp = Array(repeating: Array(repeating: 0, count: n), count: m)
        for i in 0..<m { dp[i][0] = 1 } 
        for i in 0..<n { dp[0][i] = 1 } 
        for i in 1..<m {
            for j in 1..<n {
                dp[i][j] = dp[i][j - 1] + dp[i - 1][j]
            }
        }
        return dp[m - 1][n - 1]
    }
}

题目2:63.不同路径II

讲解
leetcode

// @lc code=start
class Solution {
    // 注意审题:机器人每次只能向下或者向右移动一步
    // 纠结了半天,为什么机器人不可以往回走...
    func uniquePathsWithObstacles(_ obstacleGrid: [[Int]]) -> Int {
        let m = obstacleGrid.count
        let n = obstacleGrid[0].count
        if obstacleGrid[0][0] == 1 || obstacleGrid[m - 1][n - 1] == 1 { return 0 }
        var dp = Array(repeating: Array(repeating: 0, count: n), count: m)
        for i in 0..<m {
            if obstacleGrid[i][0] == 1 { break }
            dp[i][0] = 1
        }
        for i in 0..<n {
            if obstacleGrid[0][i] == 1 { break }
            dp[0][i] = 1
        }
        for i in 1..<m {
            for j in 1..<n {
                if obstacleGrid[i][j] == 1 { continue }
                dp[i][j] = dp[i - 1][j] + dp[i][j - 1]
            }
        }
        return dp[m - 1][n - 1]
    }
}
// @lc code=end

// 空间省略
class Solution {
    // 注意审题:机器人每次只能向下或者向右移动一步
    // 纠结了半天,为什么机器人不可以往回走...
    func uniquePathsWithObstacles(_ obstacleGrid: [[Int]]) -> Int {
        let m = obstacleGrid.count
        let n = obstacleGrid[0].count
        if obstacleGrid[0][0] == 1 || obstacleGrid[m - 1][n - 1] == 1 { return 0 }
        var dp = Array(repeating: 0, count: n)
        for j in 0..<n {
            if obstacleGrid[0][j] == 1 {
                break  // 碰到障碍物之后,后续的路径数都为 0
            }
            dp[j] = 1
        }
        for i in 1..<m {
            for j in 0..<n {
                if obstacleGrid[i][j] == 1 { 
                    dp[j] = 0 
                } else if j > 0 { dp[j] += dp[j-1] }
            }
        }
        return dp[n - 1]
    }
}

题目3: 343.整数拆分

讲解
leetcode

此题非常典型:
dp方程也有一定难度:定义为 dp[i] 为i拆分后的最大乘积。
所以递推方程为以下几个结果的最大者:

  1. dp[i]本身, 因为一直在遍历,所以保留最大者
  2. 将i拆成两个数 j * (i - j)
  3. 将i拆分成两个数之后,用dp[i - j]乘以 j。因为 dp[i - j]即为 i-j位置上的最大乘积。

因为要求n,数组下标从0开始,所以dp初始化n+1个元素。
初始化直接按照题目要求 dp[2] = 1, 越过 0 1两个元素即可。
遍历的时候从1开始,0没有意义。

// @lc code=start
class Solution {
    func integerBreak(_ n: Int) -> Int {    
        if n <= 3 { return n - 1 }
        var dp = Array(repeating: 0, count: n + 1)
        dp[2] = 1
        for i in 3...n {
            for j in 1..<i {
                dp[i] = max(dp[i], j * (i - j), j * dp[i - j])
            }
        }
        return dp[n]
    }
}
// @lc code=end

题目4:96.不同的二叉搜索树

讲解
leetcode
相当不好想。

// @lc code=start
class Solution {
    func numTrees(_ n: Int) -> Int {
        var dp = Array(repeating: 0, count: n + 1)
        dp[0] = 1
        // 这里的边界条件可以根据 i 为3进行推导: dp[0] * dp[2] + dp[1] * dp[1] + dp[2] * dp[0]
        for i in 1...n {
            for j in 1...i {
                dp[i] += dp[j - 1] * dp[i - j]
            }
        }
        return dp[n]
    }
}
// @lc code=end