题目描述

✅ LCR 089. 打家劫舍

image-20260929004142307

image-20260929004142309

题意分析

在非负整数数组中选择一些下标,要求任意两个被选下标都不相邻,使选中金额之和最大。没有必须选择多少间房的限制,也不要求选择最后一间。

单看某间房的金额无法决定是否选它,因为放弃它可能换来两侧更大的总收益。可以按当前房屋“选或不选”分类,把问题转化为较短前缀的最优值。

解法:前缀最优的选与不选

核心思路

[!blue]

f[i] 表示只考虑前 i 间房,即 nums[0..i-1] 时的最大合法金额。它包含最后一间选与不选的两种可能,不表示一定选择了 nums[i-1]。

处理第 i 间房时分两种情况:不选它,剩下的最优值就是 f[i-1];选它,就必须排除第 i-1 间房,前面可以接上前 i-2 间的任意合法方案,最优值为 f[i-2] + nums[i-1]。因此:

f[i] = max(f[i-1], f[i-2] + nums[i-1])。

任何合法方案都属于其中一个分支,而两个分支构造出的方案也都不会出现相邻冲突。对已经确定的分支,用对应前缀的最优方案替换较差方案不会影响当前选择,所以只保留最优金额就足够,不必记录具体偷了哪些房屋。

f[0] = 0 表示没有房屋,f[1] = nums[0] 利用了金额非负这一条件。从 i = 2 向后计算时,前两个依赖值已经求出;最终 f[n] 就覆盖整排房屋的全部选择。只有一间房时循环不执行,直接得到它的金额;全零输入也自然返回零。

解题步骤

  1. 创建长度为 n+1 的数组,令 f[0] = 0、f[1] = nums[0]。
  2. 从前两间开始递推,比较不选当前房屋与选当前、排除相邻房屋的收益。
  3. 计算到前 n 间后,返回 f[n]。

代码实现

class Solution {
    public int rob(int[] nums) {
        int n = nums.length;
        // f[i]:只考虑前 i 家能偷到的最大金额;第 i 家对应 nums[i - 1]。
        int[] f = new int[n + 1];

        // f[0] = 0 由默认零值给出;只有一家时必偷。
        f[1] = nums[0];

        for (int i = 2; i <= n; ++i) {
            // 左:不偷第 i 家;右:偷第 i 家,必须跳过第 i - 1 家。
            f[i] = Math.max(f[i - 1], f[i - 2] + nums[i - 1]);
        }

        return f[n];
    }
}
func rob(nums []int) int {
    n := len(nums)
    // f[i]:只考虑前 i 家能偷到的最大金额;第 i 家对应 nums[i-1]。
    f := make([]int, n+1)
    // f[0] = 0 由默认零值给出;只有一家时必偷。
    f[1] = nums[0]
    for i := 2; i <= n; i++ {
        // 左:不偷第 i 家;右:偷第 i 家,必须跳过第 i-1 家。
        f[i] = max(f[i-1], f[i-2]+nums[i-1])
    }
    return f[n]
}

复杂度分析

  • 时间复杂度:$O(n)$,每个前缀只计算两个候选值。
  • 空间复杂度:$O(n)$,保存 n+1 个前缀状态;只需返回金额时,也可以用两个变量保存相邻状态。

关键点总结

[!green]

  • 状态按房屋数量编号,前 i 间中的最后一间是 nums[i-1]。
  • 选当前房屋时接 f[i-2],通过排除邻居保证任意前缀最优方案都能与当前选择共存。
  • f[i] 已经比较选与不选,不需要强制最后一间被选,也不需要再遍历所有前缀取最大值。

易错点总结

[!yellow]

  • 选当前房屋时若加上 f[i-1],这个前缀可能已经选择了相邻房屋,会得到非法金额。
  • 不能混淆前缀数量下标与数组下标,转移中当前金额是 nums[i-1]。
  • 按单间金额贪心,可能错过两侧不相邻房屋的更大总收益。

相似题目

题目 难度 关联与区别
213. 打家劫舍 II 中等 增加首尾相邻条件后,不能同时选择两端,需要拆成两条线性区间。
337. 打家劫舍 III 中等 把线性相邻限制扩展到父子节点不能同时选择,状态仍是选与不选。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2026/90224472
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!