Description

給定股票每天的價格陣列,可以多次買賣但賣出後隔天為冷卻期(不能買入)。求最大利潤。

Example:

Input: prices = [1,2,3,0,2] Output: 3 (買 1 賣 3,冷卻,買 0 賣 2)

Intuition#

用狀態機思維:每天有「持有股票」、「不持有且在冷卻」、「不持有且非冷卻」三種狀態。

  • 定義三種狀態:
    • hold:持有股票的最大利潤
    • sold:今天剛賣出(明天進入冷卻)
    • rest:不持有且非冷卻(可以買入)
  • 狀態轉移:
    • hold = max(hold, rest - price) (繼續持有 或 今天買入)
    • sold = hold + price (今天賣出)
    • rest = max(rest, sold) (繼續休息 或 冷卻結束)

Approaches#

1: 2D DP Table#

  • 概念: 用 dp[i][state] 記錄第 i 天各狀態的最大利潤,state 為 0(rest)、1(hold)、2(sold)。
  • 時間複雜度: O(n)
  • 空間複雜度: O(n)
class Solution {
    fun maxProfit(prices: IntArray): Int {
        val n = prices.size
        if (n <= 1) return 0
        // dp[i][0] = rest, dp[i][1] = hold, dp[i][2] = sold
        val dp = Array(n) { IntArray(3) }
        dp[0][0] = 0
        dp[0][1] = -prices[0]
        dp[0][2] = 0
        for (i in 1 until n) {
            dp[i][0] = maxOf(dp[i - 1][0], dp[i - 1][2])
            dp[i][1] = maxOf(dp[i - 1][1], dp[i - 1][0] - prices[i])
            dp[i][2] = dp[i - 1][1] + prices[i]
        }
        return maxOf(dp[n - 1][0], dp[n - 1][2])
    }
}

⭐2: 狀態機(空間優化)#

  • 概念: 三個狀態只依賴前一天的值,用三個變數即可。
  • 時間複雜度: O(n)
  • 空間複雜度: O(1)
class Solution {
    fun maxProfit(prices: IntArray): Int {
        if (prices.size <= 1) return 0
        var rest = 0
        var hold = -prices[0]
        var sold = 0
        for (i in 1 until prices.size) {
            val prevRest = rest
            val prevHold = hold
            val prevSold = sold
            rest = maxOf(prevRest, prevSold)
            hold = maxOf(prevHold, prevRest - prices[i])
            sold = prevHold + prices[i]
        }
        return maxOf(rest, sold)
    }
}

🔑 Takeaways#

  • Pattern: 狀態機 DP — 定義多個狀態並描述轉移關係
  • 關鍵技巧: 股票問題系列都可以用狀態機建模(持有/未持有/冷卻);注意更新順序,需要用前一天的值