213. 打家劫舍 II(中等)
题目
给定一个非负整数数组,表示若干户房屋里的金额。这些房屋排成一圈:也就是说,第 1 户与最后一户也是邻居。
如果任意两户相邻(包括首尾这一对)都同时被你光顾,就会触发警报。在不报警的前提下,问:今晚最多能拿到多少钱?
换句话说:在一串首尾相接的数字里选若干项,使得选中的两项都不相邻,求选中项之和的最大值。
自造示例:[3,2,3,1] → 6。最优是偷第 1 户和第 3 户:3 + 3 = 6;其余选择要么首尾或相邻冲突,要么金额更小。
再如只有一户时没有环冲突:[5] → 5。
约束:1 ≤ nums.length ≤ 100;0 ≤ nums[i] ≤ 1000。
思路
一句话:环形只比一条直线多一条“首尾不能同时偷”的限制;把环剪成两段,分别求两段直线上的最优值,再取较大者。
先想最直白的做法:枚举所有“偷 / 不偷”的子集,再逐个检查有没有相邻。共有 2n 种子集,即使用位运算判断冲突也要爆掉。
再回想直线版“打家劫舍”怎么解:走到第 i 户,只有两种选择——
- 偷第
i户:第i-1户不能偷,收益是dp[i-2] + nums[i]; - 不偷第
i户:收益沿用dp[i-1]。
于是 dp[i] = max(dp[i-1], dp[i-2] + nums[i]);因为只依赖前两项,可以用两个滚动变量把空间压到 O(1)。
关键观察:成环以后,最优方案不可能同时偷第 1 户和最后一户——它俩相邻,同偷一定报警。既然“首尾互斥”,就可以按二选一分类:
- 不要最后一户:问题退化为在
nums[0..n-2]上求直线版; - 不要第一户:问题退化为在
nums[1..n-1]上求直线版。
两类合起来覆盖了所有合法方案,所以取两者的最大值就是答案。唯一要单独处理的是 n == 1:此时“第一户”和“最后一户”是同一户,不存在环冲突,直接返回 nums[0]。
解法
把直线版打包成一个辅助函数 robLine(lo, hi),内部滚两个变量:
- 若数组只有 1 个数,直接返回该数。
- 分别计算
robLine(0, n-2)与robLine(1, n-1)。 robLine初始化prev2 = prev1 = 0,分别代表dp[i-2]、dp[i-1];每走一步算出cur = max(prev1, prev2 + nums[i]),再滚动。- 返回
max(两段结果)。
以 [3,2,3,
本篇为付费内容
您已阅读试读部分,购买后即可解锁全文。
⚠️ 本站内容已做唯一性标记并受版权保护,未经授权禁止转载,侵权必究。
还没有评论,来说两句?