Hard草稿★★★★★O(n^3) 時間 · O(n^2) 空間
Dynamic ProgrammingInterval DP
Patterns🪟 區間 DP🔲 網格・雙序列 DP
尚未複習過

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

312Burst Balloons2-D DPHard2-D DP

給定 n 個氣球,每個氣球上有一個數字。戳破氣球 i 可獲得 nums[i-1] * nums[i] * nums[i+1] 的硬幣,戳破後左右氣球變為相鄰。求能收集的最大硬幣數。

Example:

Input: nums = [3,1,5,8] Output: 167

Intuition

TIP

逆向思考:不是決定先戳哪個,而是決定在區間 (i, j) 中「最後戳哪個」氣球 k。

Approaches

1. Memoized Search (Top-Down) — O(n^3) / O(n^2)
  • Idea: 遞迴 + memo,枚舉區間內最後被戳破的氣球。
  • Time: O(n^3)
  • Space: O(n^2)
class Solution {
    fun maxCoins(nums: IntArray): Int {
        val n = nums.size
        val extended = IntArray(n + 2)
        extended[0] = 1
        extended[n + 1] = 1
        for (i in nums.indices) extended[i + 1] = nums[i]
        val memo = Array(n + 2) { IntArray(n + 2) { -1 } }
        return dp(extended, memo, 0, n + 1)
    }

    private fun dp(nums: IntArray, memo: Array<IntArray>, left: Int, right: Int): Int {
        if (left + 1 == right) return 0
        if (memo[left][right] != -1) return memo[left][right]
        var result = 0
        for (k in left + 1 until right) {
            result = maxOf(
                result,
                dp(nums, memo, left, k) + dp(nums, memo, k, right) +
                    nums[left] * nums[k] * nums[right]
            )
        }
        memo[left][right] = result
        return result
    }
}
⭐ 2. Interval DP (Bottom-Up) — O(n^3) / O(n^2)
  • Idea: 按區間長度從小到大填表,dp[i][j] 為開區間 (i, j) 的最大硬幣數。
  • Time: O(n^3)
  • Space: O(n^2)
class Solution {
    fun maxCoins(nums: IntArray): Int {
        val n = nums.size
        val extended = IntArray(n + 2)
        extended[0] = 1
        extended[n + 1] = 1
        for (i in nums.indices) extended[i + 1] = nums[i]

        val dp = Array(n + 2) { IntArray(n + 2) }
        for (len in 2..n + 1) {
            for (i in 0..n + 1 - len) {
                val j = i + len
                for (k in i + 1 until j) {
                    dp[i][j] = maxOf(
                        dp[i][j],
                        dp[i][k] + dp[k][j] + extended[i] * extended[k] * extended[j]
                    )
                }
            }
        }
        return dp[0][n + 1]
    }
}

🔑 Takeaways