Hard草稿★★★★★O(n) 時間 · O(1) 空間
Dynamic ProgrammingMathCombinatorics
Patterns📈 線性 DP・Kadane🧮 數學・組合
尚未複習過

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

1359Count all Valid Pickup and Delivery Options1-D DPHard1-D DP

給定 n 個訂單,每個有取件 (Pi) 和送件 (Di)。取件必須在送件之前。求所有合法排列的數量,結果對 10^9 + 7 取模。

Example:

Input: n = 2 Output: 6

Intuition

TIP

增量法:已排好 i-1 個訂單後,第 i 個訂單的 (Pi, Di) 有 (2i-1) * 2i / 2 種插入方式。

Approaches

1. Memoization (Top-Down DP) — O(n) / O(n)
  • Idea: 直接照定義遞迴 f(i) = f(i-1) * (2i-1) * i,加 memo 快取
  • Time: O(n) - 每個狀態只算一次
  • Space: O(n) - memo 陣列 + 遞迴堆疊
class Solution {
    fun countOrders(n: Int): Int {
        val MOD = 1_000_000_007L
        val memo = LongArray(n + 1) { -1L }
        fun dp(i: Int): Long {
            if (i == 1) return 1L
            if (memo[i] != -1L) return memo[i]
            memo[i] = dp(i - 1) * (2L * i - 1) % MOD * i % MOD
            return memo[i]
        }
        return dp(n).toInt()
    }
}
2. Bottom-Up DP — O(n) / O(n)
  • Idea: dp[i] = dp[i-1] * (2i-1) * i,逐步累乘
  • Time: O(n)
  • Space: O(n)
class Solution {
    fun countOrders(n: Int): Int {
        val MOD = 1_000_000_007L
        val dp = LongArray(n + 1)
        dp[1] = 1
        for (i in 2..n) {
            val slots = 2L * i - 1
            dp[i] = dp[i - 1] * slots % MOD * i % MOD
        }
        return dp[n].toInt()
    }
}
⭐ 3. Math (Rolling Multiplication) — O(n) / O(1)
  • Idea: 直接用變數累乘,不需陣列
  • Time: O(n)
  • Space: O(1)
class Solution {
    fun countOrders(n: Int): Int {
        val MOD = 1_000_000_007L
        var result = 1L
        for (i in 2..n) {
            // 插入第 i 個訂單:Pi 有 (2i-1) 個空位
            // Di 必須在 Pi 之後,平均有 i 個位置
            // 方案數 = (2i-1) * i
            result = result * (2L * i - 1) % MOD * i % MOD
        }
        return result.toInt()
    }
}

🔑 Takeaways