198. 打家劫舍(中等)
Blind 75 › 198. 打家劫舍(中等) (第 2 章)
题目
沿街一排放着 n 间房,每间藏着一笔钱,用数组 nums 给出。规则只有一条:相邻两间不能同时偷,否则会触发报警。问这一晚最多能到手多少钱。
换个说法:从数组里挑一组下标互不相邻的位置,使这些位置上的数字之和最大。
自造示例:nums = [10, 4, 3, 12] → 22(偷 nums[0]=10 和 nums[3]=12)。nums = [2, 1, 1, 2] → 4(偷 nums[0]=2 和 nums[3]=2)。
约束:1 ≤ n ≤ 100;0 ≤ nums[i] ≤ 400;所有金额为非负整数。
思路
一句话:每间房只有「偷」和「不偷」两种选择;求「前 i 间的最优」时,答案只依赖前两步,可以一路滚动。
先想最直白的:枚举所有“互不相邻”的下标组合,求和取最大。每个位置都分出偷/不偷两条路,像一棵二叉树,规模大约是 O(2n),n=100 时彻底不可行;即使只枚举合法组合,数量也是斐波那契级别,仍是指数增长。
那能不能贪心:每次都挑金额最大的房,再把相邻的划掉?不行。比如 [2, 3, 2],贪心先拿中间的 3,两边都不能碰,只得 3;实际上拿两头的 2 + 2 = 4 更优。
关键观察:问题有重叠子问题和无后效性。考虑最后一间 i:
- 偷 i:那么 i-1 一定不能偷,答案 = 「前 i-2 间的最优」 +
nums[i]。 - 不偷 i:答案 = 「前 i-1 间的最优」。
于是 dp[i] = max(dp[i-1], dp[i-2] + nums[i])。而 dp[i] 只依赖前两项,用两个变量滚动即可,连 dp 数组都不需要。
解法
维护两个变量 prev2(到 i-2 为止的最优)和 prev1(到 i-1 为止的最优),从左到右扫一遍:
- 算当前最优:
cur = max(prev2 + nums[i], prev1)。 - 滚动:
prev2 = prev1,prev1 = cur。 - 扫完返回
prev1。
以 [10, 4, 3, 12] 为例:
| 下标 i | nums[i] | prev2 + nums[i] | prev1 | cur = max(…) |
|---|---|---|---|---|
| 0 | 10 | 0 + 10 = 10 | 0 | 10 |
| 1 | 4 | 0 + 4 = 4 | 10 | 10 |
| 2 | 3 | 10 + 3 = 13 | 10 | 13 |
| 3 | 12 | 10 + 12 = 22 | 13 | 22 |
最后 prev1 = 22,对应方案是偷下标 0 和 3。
下图把上面 3 个步骤串成流程,并逐步演示:


C++
class Solution {
public:
int rob(vector<int>& nums) {
int prev2 = 0; // 到 i-2 为止的最优
int prev1 = 0; // 到 i-1 为止的最优
for (int x : nums) {
int cur = max(prev2 + x, prev1); // 偷当前 / 不偷当前,取更大
prev2 = prev1;
prev1 = cur;
}
return prev1;
}
};Python
Python 3 示例
还没有评论,来说两句?