Hard草稿★★★★★O(n·k) 時間 · O(k) 空間
ArrayDynamic Programming
Patterns🔲 網格・雙序列 DP
尚未複習過

解法已隱藏 — 先讀題目敘述、自己想想看,再點上方按鈕揭曉。

188Best Time to Buy and Sell Stock IV2-D DPHard2-D DP

給定每日股價 prices 與整數 k最多完成 k 筆交易,求最大利潤(同一時間最多持有一股,先賣才能再買)。

Example:

Input: k = 2, prices = [2,4,1] Output: 2(第 1 天買 2、第 2 天賣 4)

Input: k = 2, prices = [3,2,6,5,0,3] Output: 7(買 2 賣 6 賺 4;買 0 賣 3 賺 3)

Intuition

TIP

核心思路:123 的一般化——用 buy[j]sell[j] 兩個長度 k+1 的陣列。另外當 k 大到 ≥ n/2,等於不限次數,退化成貪心收所有上漲段。

Approaches

⭐ k-Transaction DP + Large-k Greedy Shortcut — O(n·k) / O(k)
  • Idea: 內層對 j = 1..k 更新 buy/sell;k 過大時改用無限次交易的貪心
  • Time: O(n·k)(大 k 時 O(n)
  • Space: O(k) - 兩個長度 k+1 陣列
class Solution {
    fun maxProfit(k: Int, prices: IntArray): Int {
        val n = prices.size
        if (n == 0 || k == 0) return 0

        // k 夠大 → 等於不限次數,收所有上漲段
        if (k >= n / 2) {
            var profit = 0
            for (i in 1 until n) {
                if (prices[i] > prices[i - 1]) profit += prices[i] - prices[i - 1]
            }
            return profit
        }

        val buy = IntArray(k + 1) { Int.MIN_VALUE }   // 第 j 次買後最大現金
        val sell = IntArray(k + 1)                    // 第 j 次賣後最大利潤
        for (p in prices) {
            for (j in 1..k) {
                buy[j] = maxOf(buy[j], sell[j - 1] - p)
                sell[j] = maxOf(sell[j], buy[j] + p)
            }
        }
        return sell[k]
    }
}
Note: Why inner loop ascends from j = 1

buy[j] 依賴同一輪剛更新的 sell[j-1](同一天可從第 j-1 次賣的狀態接著做第 j 次買),所以 j 由小到大更新是對的;而 sell[j] 依賴同一輪的 buy[j],順序一致不會用到尚未更新的未來狀態。

🔑 Takeaways