BM79 打家劫舍(二)¶
中等通过率 39.97%python3核心代码模式样例通过牛客 AC动态规划
讲解章节:线性 DP
一句话
首尾相邻,拆成「不偷最后一家」与「不偷第一家」两条直链各跑一遍。
解题思路¶
这题考什么¶
环形约束的通用拆解手法:枚举一个足以打破环的决策,把环拆成若干条直链。
本题比 BM78 只多一条约束:第 1 家与第 n 家相邻,不能同时偷。 这条约束等价于「第一家与最后一家至少有一家没被偷」,于是按这一点分两种情况:
不重不漏从两个方向确认:
- 不漏:任何合法方案要么没偷最后一家(落进 A),要么没偷第一家(落进 B);
- 不错:A、B 两条链里第一家与最后一家不可能同时出现, 所以两条链产出的方案全都是合法的。
两条链有重叠(首尾都不偷的方案在 A、B 里各算一次)不影响取最大值。
线性部分的状态与转移完全同 BM78:
同样用 prev、cur 两个变量滚动。
数据规模与复杂度¶
1 <= n <= 2e5,1 <= nums[i] <= 5000,时限 4 秒(其他语言)。 跑两遍线性 DP,时间 O(n)、空间 O(1)。
坑在哪¶
- n == 1 必须单独返回 nums[0]。此时两条链 nums[0:0] 与 nums[1:1] 都是空的, 照常跑会返回 0——而唯一的一家并不与自己相邻,本来就该偷。
- n == 2 不必特判:A = nums[0]、B = nums[1],取大者正好是「只能偷一家」的正解。
- 不能用「先按直链求解,再对同时偷了首尾的方案打补丁」的做法。 去掉首尾之一后的最优解未必只差这一处,补丁式修正无法保证仍是最优。
- 两条链共用一个 line 函数,靠 [lo, hi) 的半开区间划出范围; 写成闭区间会让两条链各差一家,answer 偏小或非法。
- 与 BM78 同源的陷阱:prev、cur 必须同时更新,拆成两条语句就允许偷到相邻两家。
样例复核¶
[1,2,3,4]:A 在 [1,2,3] 上得 4(偷 1 和 3),B 在 [2,3,4] 上得 6(偷 2 和 4), 取 6,与期望输出一致。
参考实现¶
[:octicons-arrow-left-16: BM78](BM78.md) [BM80 :octicons-arrow-right-16:](BM80.md)