Hard草稿★★★★★O(n log n) 時間 · O(n) 空間
ArrayBinary SearchDynamic ProgrammingSorting
Patterns🎯 二分搜尋📈 線性 DP・Kadane🔢 排序後處理
尚未複習過

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

354Russian Doll Envelopes1-D DPHard1-D DP

每個信封有寬 w 與高 h,只有當一個信封的寬與高都嚴格大於另一個時才能套入。求最多能套幾層(俄羅斯娃娃)。

Example:

Input: envelopes = [[5,4],[6,4],[6,7],[2,3]] Output: 3([2,3] → [5,4] → [6,7])

Intuition

TIP

核心思路:先把寬度排序,問題就化為對高度求「最長嚴格遞增子序列(LIS)」。關鍵小技巧:寬度相同時,高度要遞減排序,避免同寬被誤算成可巢狀。

Approaches

1. Sort + O(n²) DP LIS — O(n²) / O(n)
  • Idea: 排序後對高度做標準 LIS DP
  • Time: O(n²) - 大 n 會 TLE,僅作理解
  • Space: O(n)
class Solution {
    fun maxEnvelopes(envelopes: Array<IntArray>): Int {
        envelopes.sortWith(compareBy({ it[0] }, { -it[1] }))
        val n = envelopes.size
        val dp = IntArray(n) { 1 }
        var ans = 0
        for (i in 0 until n) {
            for (j in 0 until i) {
                if (envelopes[j][1] < envelopes[i][1]) dp[i] = maxOf(dp[i], dp[j] + 1)
            }
            ans = maxOf(ans, dp[i])
        }
        return ans
    }
}
⭐ 2. Sort + LIS Binary Search (patience) — O(n log n) / O(n)
  • Idea: 對高度跑二分版 LIS,維護 tails 陣列
  • Time: O(n log n) - 排序 + 每個元素二分
  • Space: O(n)
class Solution {
    fun maxEnvelopes(envelopes: Array<IntArray>): Int {
        // 寬升序;同寬高降序(避免同寬被當成可套)
        envelopes.sortWith(compareBy({ it[0] }, { -it[1] }))
        val tails = ArrayList<Int>()  // tails[k] = 長度 k+1 的遞增子序列的最小結尾高度
        for (e in envelopes) {
            val h = e[1]
            var lo = 0; var hi = tails.size
            while (lo < hi) {
                val mid = (lo + hi) ushr 1
                if (tails[mid] < h) lo = mid + 1 else hi = mid
            }
            if (lo == tails.size) tails.add(h) else tails[lo] = h
        }
        return tails.size
    }
}

🔑 Takeaways