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

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

1547Minimum Cost to Cut a Stick2-D DPHard2-D DP

給定一根長度為 n 的木棍和一組切割點 cuts。每次切割的成本等於被切的那段木棍長度。可以任意順序切割,回傳最小總成本。

Example:

Input: n = 7, cuts = [1,3,4,5] Output: 16

Intuition

TIP

區間 DP:dp[i][j] 表示切完第 i 到第 j 個切割點之間所有切割的最小成本。

Approaches

1. Memoized Search — O(c^3) / O(c^2)
  • Idea: Top-down 遞迴,dfs(i, j) 回傳切完 cuts[i]cuts[j] 之間的最小成本。
  • Time: O(c^3),c 為切割點數
  • Space: O(c^2)
class Solution {
    fun minCost(n: Int, cuts: IntArray): Int {
        val sortedCuts = (intArrayOf(0) + cuts.sorted() + intArrayOf(n))
        val m = sortedCuts.size
        val memo = Array(m) { IntArray(m) { -1 } }

        fun dfs(i: Int, j: Int): Int {
            if (j - i <= 1) return 0
            if (memo[i][j] != -1) return memo[i][j]

            var best = Int.MAX_VALUE
            for (k in i + 1 until j) {
                best = minOf(best, dfs(i, k) + dfs(k, j) + sortedCuts[j] - sortedCuts[i])
            }
            memo[i][j] = best
            return best
        }

        return dfs(0, m - 1)
    }
}
⭐ 2. Bottom-Up Interval DP — O(c^3) / O(c^2)
  • Idea: 從短區間到長區間填表,dp[i][j] 為切完 cuts[i]cuts[j] 之間的最小成本。
  • Time: O(c^3)
  • Space: O(c^2)
class Solution {
    fun minCost(n: Int, cuts: IntArray): Int {
        val sortedCuts = (intArrayOf(0) + cuts.sorted() + intArrayOf(n))
        val m = sortedCuts.size
        val dp = Array(m) { IntArray(m) }

        for (len in 2 until m) {
            for (i in 0 until m - len) {
                val j = i + len
                dp[i][j] = Int.MAX_VALUE
                for (k in i + 1 until j) {
                    dp[i][j] = minOf(dp[i][j], dp[i][k] + dp[k][j] + sortedCuts[j] - sortedCuts[i])
                }
            }
        }
        return dp[0][m - 1]
    }
}

🔑 Takeaways