198. House Robber

練習日期:2026-03-04

難度:Medium

類型:Array、Dynamic Programming

📘 題目敘述

你是一個專業小偷,計畫沿著一條街偷房子。每間房子都藏有一定金額,唯一的限制是:相鄰的房子有連動的保全系統,如果同一個晚上有兩間相鄰房子都被闖入,系統就會自動報警。

給你一個整數陣列 nums,其中 nums[i] 代表第 i 間房子裡的金額。請回傳你今晚在不觸發警報的情況下,能偷到的最大金額。

條件限制

  • 1 <= nums.length <= 100
  • 0 <= nums[i] <= 400

🧠 解題思路

這題是典型的 DP:每一間房子只有「偷」或「不偷」兩種狀態,而且「偷第 i 間」會影響到「第 i-1 間不能偷」。

所以我把狀態拆成兩個 DP 陣列,讓邏輯跟程式碼對得上:

  • dp0[i]:走到第 i 間時,第 i 間不偷 的最大金額
  • dp1[i]:走到第 i 間時,第 i 間有偷 的最大金額

有了這兩個狀態後,轉移就很直覺:

  • 如果第 i 間不偷:

    那我只要承接前一間「偷或不偷」的最大值即可
    dp0[i] = max(dp0[i-1], dp1[i-1])

  • 如果第 i 間要偷:

    前一間一定不能偷,所以只能從 dp0[i-1] 轉移
    dp1[i] = nums[i] + dp0[i-1]

最後答案就是最後一間偷或不偷的最大值。

所有變數

  • n:房子數量(nums.size()
  • dp0dp0[i] 表示走到第 i 間且第 i 間不偷時的最大金額
  • dp1dp1[i] 表示走到第 i 間且第 i 間有偷時的最大金額

🪜 主要流程步驟

1. 初始化第 0 間房子的兩種狀態

第 0 間只有兩種情況:

  • 不偷:dp0[0] = 0
  • 偷:dp1[0] = nums[0]

2. 依序處理第 1 間到第 n-1 間

對每個 i >= 1

先算「不偷第 i 間」:

dp0[i] = max(dp0[i - 1], dp1[i - 1])

意思是:第 i 間不偷,我就沿用前一間最好的結果(前一間偷或不偷都可以)。

再算「偷第 i 間」:

dp1[i] = nums[i] + dp0[i - 1]

意思是:第 i 間要偷,前一間一定要不偷,所以只能接 dp0[i-1]


3. 回傳最後一間的最大值

最後答案就是:

max(dp0[n - 1], dp1[n - 1])

代表最後一間偷或不偷都考慮進去,取最大。

⏱️ 複雜度

  • 時間複雜度:O(n)
    只掃一次房子。
  • 空間複雜度:O(n)
    兩個 DP 陣列各長度 n

💻 程式碼實作

class Solution {
public:
    int rob(vector<int>& nums) {
        int n = nums.size();
        vector<int> dp0(n); // 沒選
        vector<int> dp1(n); // 有選
        dp0[0] = 0;
        dp1[0] = nums[0];
        for (int i = 1; i < n; i++) {
            dp0[i] = max(dp0[i - 1], dp1[i - 1]);
            dp1[i] = nums[i] + dp0[i - 1];
        }
        return max(dp0[n - 1], dp1[n - 1]);
    }
};

https://leetcode.com/problems/house-robber/

作者: scottnick
撰寫日期: 2026-03-04
類別:
原文連結: https://scottnick.github.io/cpp-notes/neetcode150/198-house-robber.html