Medium草稿★★★★★O(n) 時間 · O(1) 空間
Dynamic ProgrammingArray
Patterns📈 線性 DP・Kadane
尚未複習過

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

198House Robber1-D DPMedium1-D DP

一排房屋各有不同金額,相鄰的房屋不能同時被搶。給定代表每間房屋金額的整數陣列 nums,求在不觸發警報的情況下能搶到的最大金額。

Example:

Input: nums = [2,7,9,3,1] Output: 12

Intuition

TIP

每間房屋只有兩種選擇:搶或不搶。搶了就跳過下一間,不搶則繼續。

Approaches

1. Memoization (Top-Down DP) — O(n) / O(n)
  • Idea: 從第 0 間房屋開始遞迴,每間房屋兩種選擇:搶(跳到 i+2)或不搶(跳到 i+1),用 memo 快取重複子問題
  • Time: O(n) - 每個狀態只算一次
  • Space: O(n) - memo 陣列 + 遞迴堆疊
class Solution {
    fun rob(nums: IntArray): Int {
        val memo = IntArray(nums.size) { -1 }
        return dp(nums, 0, memo)
    }

    private fun dp(nums: IntArray, i: Int, memo: IntArray): Int {
        if (i >= nums.size) return 0
        if (memo[i] != -1) return memo[i]
        memo[i] = maxOf(nums[i] + dp(nums, i + 2, memo), dp(nums, i + 1, memo))
        return memo[i]
    }
}
2. Bottom-Up DP Array — O(n) / O(n)
  • Idea: 建立 DP 陣列紀錄到每間房屋為止的最大收益
  • Time: O(n)
  • Space: O(n)
class Solution {
    fun rob(nums: IntArray): Int {
        if (nums.size == 1) return nums[0]
        val dp = IntArray(nums.size)
        dp[0] = nums[0]
        dp[1] = maxOf(nums[0], nums[1])
        for (i in 2 until nums.size) {
            dp[i] = maxOf(dp[i - 1], dp[i - 2] + nums[i])
        }
        return dp[nums.size - 1]
    }
}
⭐ 3. Rolling Variables — O(n) / O(1)
  • Idea: 只需要前兩個狀態,用兩個變數即可
  • Time: O(n)
  • Space: O(1)
class Solution {
    fun rob(nums: IntArray): Int {
        var prev2 = 0
        var prev1 = 0
        for (num in nums) {
            val cur = maxOf(prev1, prev2 + num)
            prev2 = prev1
            prev1 = cur
        }
        return prev1
    }
}

🔑 Takeaways

Ladders 看全部 →

🏠

打家劫舍

線性 DP → 環狀 → 樹形 DP。

  1. 1
  2. 2#213House Robber IIMedium房子排成環 → 拆成「含頭」「含尾」兩段
  3. 3#337House Robber IIIMedium房子是二元樹 → 樹形 DP(搶/不搶根)