本文属于「征服LeetCode」系列文章之一,这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁,本系列将至少持续到刷完所有无锁题之日为止;由于LeetCode还在不断地创建新题,本系列的终止日期可能是永远。在这一系列刷题文章中,我不仅会讲解多种解题思路及其优化,还会用多种编程语言实现题解,涉及到通用解法时更将归纳总结出相应的算法模板。
为了方便在PC上运行调试、分享代码文件,我还建立了相关的仓库。在这一仓库中,你不仅可以看到LeetCode原题链接、题解代码、题解文章链接、同类题目归纳、通用解法总结等,还可以看到原题出现频率和相关企业等重要信息。如果有其他优选题解,还可以一同分享给他人。
由于本系列文章的内容随时可能发生更新变动,欢迎关注和收藏征服LeetCode系列文章目录一文以作备忘。
你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。
给定一个代表每个房屋存放金额的非负整数数组,计算你 在不触动警报装置的情况下 ,今晚能够偷窃到的最高金额。
示例 1:
输入:nums = [2,3,2]
输出:3
解释:你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。
示例 2:
输入:nums = [1,2,3,1]
输出:4
解释:你可以先偷窃 1 号房屋(金额 = 1),然后偷窃 3 号房屋(金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
示例 3:
输入:nums = [1,2,3]
输出:3
提示:
1 <= nums.length <= 100
0 <= nums[i] <= 1000
解法 动态规划
这道题是「198. 打家劫舍」的进阶,和第 198 题的不同之处是,这道题中的房屋是首尾相连的,第一间房屋和最后一间房屋相邻,因此第一间房屋和最后一间房屋不能在同一晚上偷窃。和第 198 题相似,这道题也可以使用动态规划解决。
首先考虑最简单的情况。如果只有一间房屋,则偷窃该房屋,可以偷窃到最高总金额。如果只有两间房屋,则由于两间房屋相邻,不能同时偷窃,只能偷窃其中的一间房屋,因此选择其中金额较高的房屋进行偷窃,可以偷窃到最高总金额。注意到当房屋数量不超过两间时,最多只能偷窃一间房屋,因此不需要考虑首尾相连的问题。如果房屋数量大于两间,就必须考虑首尾相连的问题,第一间房屋和最后一间房屋不能同时偷窃。
如何才能保证第一间房屋和最后一间房屋不同时偷窃呢?如果偷窃了第一间房屋,则不能偷窃最后一间房屋,因此偷窃房屋的范围是第一间房屋到最后第二间房屋;如果偷窃了最后一间房屋,则不能偷窃第一间房屋,因此偷窃房屋的范围是第二间房屋到最后一间房屋。
考虑是否偷 n u m s [ 0 ] nums[0] nums[0] :
- 如果偷 n u m s [ 0 ] nums[0] nums[0] ,那么 n u m s [ 1 ] nums[1] nums[1] 和 n u m s [ n − 1 ] nums[n-1] nums[n−1] 不能偷,问题变成从 n u m s [ 2 ] nums[2] nums[2] 到 n u m s [ n − 2 ] nums[n-2] nums[n−2] 的非环形版本。这里官方题解计算的是 n u m s [ 0 ] nums[0] nums[0] 到 n u m s [ n − 2 ] nums[n-2] nums[n−2] ,不如直接求 n u m s [ 2 ] nums[2] nums[2] 到 n u m s [ n − 2 ] nums[n-2] nums[n−2] 。
- 如果不偷 n u m s [ 0 ] nums[0] nums[0],那么问题变成从 n u m s [ 1 ] nums[1] nums[1] 到 n u m s [ n − 1 ] nums[n-1] nums[n−1] 的非环形版本。
这两种方案覆盖了所有情况(毕竟 n u m s [ 0 ] nums[0] nums[0] 只有偷与不偷,没有第三种选择),所以取两种方案的最大值,即为答案。
class Solution {
// 198. 打家劫舍
int rob1(vector<int> &nums, int start, int end) { // [start,end) 左闭右开
int f0 = 0, f1 = 0;
for (int i = start; i < end; ++i) {
int new_f = max(f1, f0 + nums[i]);
f0 = f1;
f1 = new_f;
}
return f1;
}
public:
int rob(vector<int> &nums) {
int n = nums.size();
return max(nums[0] + rob1(nums, 2, n - 1), rob1(nums, 1, n));
}
};