Hard草稿★★★★★O(n * k) 時間 · O(k) 空間
Dynamic ProgrammingCombinatorics
Patterns🔲 網格・雙序列 DP🧮 數學・組合
尚未複習過

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

1866Number of Ways to Rearrange Sticks With K Sticks Visible2-D DPHard2-D DP

n 根高度各不相同的棍子排成一排,從左邊看過去恰好能看到 k 根。求排列方式的數量(結果對 10^9+7 取餘)。一根棍子「可見」代表它前面沒有更高的棍子。

Example:

Input: n = 3, k = 2 Output: 3

Intuition

TIP

考慮最矮的棍子:如果它放在最前面就一定可見(+1 可見),否則它被前面的棍子擋住(不可見)。

Approaches

1. 2D DP — O(n * k) / O(n * k)
  • Idea: 建立 dp[n+1][k+1] 表格,用上述轉移方程填表。
  • Time: O(n * k)
  • Space: O(n * k)
class Solution {
    fun rearrangeSticks(n: Int, k: Int): Int {
        val MOD = 1_000_000_007L
        val dp = Array(n + 1) { LongArray(k + 1) }
        dp[0][0] = 1

        for (i in 1..n) {
            for (j in 1..minOf(i, k)) {
                dp[i][j] = (dp[i - 1][j - 1] + (i - 1L) * dp[i - 1][j] % MOD) % MOD
            }
        }
        return dp[n][k].toInt()
    }
}
⭐ 2. 1D DP (Space Optimized) — O(n * k) / O(k)
  • Idea: 由於 dp[i][j] 只依賴 dp[i-1][j-1]dp[i-1][j],可以壓縮為一維陣列,逆序更新。
  • Time: O(n * k)
  • Space: O(k)
class Solution {
    fun rearrangeSticks(n: Int, k: Int): Int {
        val MOD = 1_000_000_007L
        val dp = LongArray(k + 1)
        dp[0] = 1

        for (i in 1..n) {
            for (j in minOf(i, k) downTo 1) {
                dp[j] = (dp[j - 1] + (i - 1L) * dp[j] % MOD) % MOD
            }
            dp[0] = 0
        }
        return dp[k].toInt()
    }
}

🔑 Takeaways