算法 - 动规08(Swift版本)

4 阅读2分钟

题目1:121. 买卖股票的最佳时机

讲解
leetcode

动规方程的定义 并不算好想。
思路: 既然是状态推导, 那么就应该记录不同的状态。
此题即持有 与不持有两种状态的记录。

// @lc code=start
class Solution {
    func maxProfit(_ prices: [Int]) -> Int {
        if prices.count == 1 { return  0 }
        // 分析状态: 当天是否持有。 
        // 所以每天用一个元祖记录(a, b) 
        // 其中:a代表持有股票所得最多现金, b代表不持有股票可获得的最多现金
        var dp = Array(repeating: (0, 0), count: prices.count)
        dp[0] = (-prices[0], 0)
        for i in 1..<prices.count {
            // 持有: 上一天就持有 || 今天买入
            // 只可以买卖一次,所以买入前的现金即为0
            dp[i].0 = max(dp[i - 1].0, -prices[i])
            // 未持有: 上一天就未持有 || 今天卖出
            dp[i].1 = max(dp[i - 1].1, dp[i - 1].0 + prices[i])
        }
        // 必然是最后一天不持有结果更大
        return dp[prices.count - 1].1
    }
}
// @lc code=end

题目2:122.买卖股票的最佳时机II

讲解
leetcode

与买卖股票1的区别 就在于可以买卖多次。
所以dp[i].0 的取值 如果计算上一天不持有,今天买入的话。 就应该使用dp[i - 1].1,而不是现金总数为0

// @lc code=start
class Solution {
    func maxProfit(_ prices: [Int]) -> Int {
        if prices.count == 1 { return 0 }
        // dp[i].0 第i天持有 , dp[i].1 第i天不持有 
        var dp = Array(repeating: (0, 0), count: prices.count)
        dp[0] = (-prices[0], 0)
        for i in 1..<prices.count {
            dp[i].0 = max(dp[i - 1].0, dp[i - 1].1 - prices[i])
            dp[i].1 = max(dp[i - 1].1, dp[i - 1].0 + prices[i])
        }
        return dp[prices.count - 1].1
    }
}
// @lc code=end

题目3:123.买卖股票的最佳时机III

讲解
leetcode

延续买卖股票1的思路, 需要记录所有状态进行递推。

第二次操作 基于第一次操作进行。

dp[i].3 = max(dp[i - 1].3, dp[i - 1].2 - prices[i])

// 动规递推
class Solution {
    func maxProfit(_ prices: [Int]) -> Int {
        if prices.count == 1 { return 0 }
        // 不操作、第一次买入、第一次卖出、第二次买入、第二次卖出
        var dp = Array(repeating: (0, 0, 0, 0, 0), count: prices.count)
        dp[0] = (0, -prices[0], 0, -prices[0], 0)
        for i in 1..<prices.count {
            dp[i].0 = dp[i - 1].0
            dp[i].1 = max(dp[i - 1].1, dp[i - 1].0 - prices[i])
            dp[i].2 = max(dp[i - 1].2, dp[i - 1].1 + prices[i])
            dp[i].3 = max(dp[i - 1].3, dp[i - 1].2 - prices[i])
            dp[i].4 = max(dp[i - 1].4, dp[i - 1].3 + prices[i])
        }
        return dp[prices.count - 1].4
    }
}

// 优化滚动数组节省空间
class Solution {
    func maxProfit(_ prices: [Int]) -> Int {
        if prices.count == 1 { return 0 }
        // 使用滚动数组。
        // 不操作、第一次买入、第一次卖出、第二次买入、第二次卖出
        var status = (0, -prices[0], 0, -prices[0], 0)
        for i in 1..<prices.count {
            status.4 = max(status.4, status.3 + prices[i])
            status.3 = max(status.3, status.2 - prices[i])
            status.2 = max(status.2, status.1 + prices[i])
            status.1 = max(status.1, status.0 - prices[i])
        }
        return status.4
    }
}