题目描述
你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。
给定一个代表每个房屋存放金额的非负整数数组,计算你 在不触动警报装置的情况下 ,能够偷窃到的最高金额。
示例 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 = [0]
输出:0
提示:
1 <= nums.length <= 100
0 <= nums[i] <= 1000
官方题解
方法:动态规划
首先考虑最简单的情况。如果只有一间房屋,则偷窃该房屋,可以偷窃到最高总金额。如果只有两间房屋,则由于两间房屋相邻,不能同时偷窃,只能偷窃其中的一间房屋,因此选择其中金额较高的房屋进行偷窃,可以偷窃到最高总金额。
注意到当房屋数量不超过两间时,最多只能偷窃一间房屋,因此不需要考虑首尾相连的问题。如果房屋数量大于两间,就必须考虑首尾相连的问题,第一间房屋和最后一间房屋不能同时偷窃。
如何才能保证第一间房屋和最后一间房屋不同时偷窃呢?如果偷窃了第一间房屋,则不能偷窃最后一间房屋,因此偷窃房屋的范围是第一间房屋到最后第二间房屋;如果偷窃了最后一间房屋,则不能偷窃第一间房屋,因此偷窃房屋的范围是第二间房屋到最后一间房屋。
假设数组 nums\textit{nums}nums 的长度为 nnn。如果不偷窃最后一间房屋,则偷窃房屋的下标范围是 [0,n−2][0, n-2][0,n−2];如果不偷窃第一间房屋,则偷窃房屋的下标范围是 [1,n−1][1, n-1][1,n−1]。在确定偷窃房屋的下标范围之后,即可用第 198 题的方法解决。对于两段下标范围分别计算可以偷窃到的最高总金额,其中的最大值即为在 nnn 间房屋中可以偷窃到的最高总金额。
假设偷窃房屋的下标范围是 [start,end][\textit{start},\textit{end}][start,end],用 dp[i]\textit{dp}[i]dp[i] 表示在下标范围 [start,i][\textit{start},i][start,i] 内可以偷窃到的最高总金额,那么就有如下的状态转移方程:
dp[i]=max(dp[i−2]+nums[i],dp[i−1])\textit{dp}[i] = \max(\textit{dp}[i-2]+\textit{nums}[i], \textit{dp}[i-1])dp[i]=max(dp[i−2]+nums[i],dp[i−1])
dp[i]=max(dp[i−2]+nums[i],dp[i−1])dp[i]=max(dp[i−2]+nums[i],dp[i−1])dp[i]=max(dp[i−2]+nums[i],dp[i−1])
边界条件为:
{dp[start]=nums[start]只有一间房屋,则偷窃该房屋dp[start+1]=max(nums[start],nums[start+1])只有两间房屋,偷窃其中金额较高的房屋\begin{cases} \textit{dp}[\textit{start}] = \textit{nums}[\textit{start}] & 只有一间房屋,则偷窃该房屋 \\ \textit{dp}[\textit{start}+1] = \max(\textit{nums}[\textit{start}], \textit{nums}[\textit{start}+1]) & 只有两间房屋,偷窃其中金额较高的房屋 \end{cases}{dp[start]=nums[start]dp[start+1]=max(nums[start],nums[start+1])只有一间房屋,则偷窃该房屋只有两间房屋,偷窃其中金额较高的房屋
计算得到 dp[end]\textit{dp}[\textit{end}]dp[end] 即为下标范围 [start,end][\textit{start},\textit{end}][start,end] 内可以偷窃到的最高总金额。
分别取 (start,end)=(0,n−2)(\textit{start},\textit{end})=(0,n-2)(start,end)=(0,n−2) 和 (start,end)=(1,n−1)(\textit{start},\textit{end})=(1,n-1)(start,end)=(1,n−1) 进行计算,取两个 dp[end]\textit{dp}[\textit{end}]dp[end] 中的最大值,即可得到最终结果。
根据上述思路,可以得到时间复杂度 O(n)O(n)O(n) 和空间复杂度 O(n)O(n)O(n) 的实现。考虑到每间房屋的最高总金额只和该房屋的前两间房屋的最高总金额相关,因此可以使用滚动数组,在每个时刻只需要存储前两间房屋的最高总金额,将空间复杂度降到 O(1)O(1)O(1)。
代码
C++
class Solution {
public:
int robRange(vector<int>& nums, int start, int end) {
int first = nums[start], second = max(nums[start], nums[start + 1]);
for (int i = start + 2; i <= end; i++) {
int temp = second;
second = max(first + nums[i], second);
first = temp;
}
return second;
}
int rob(vector<int>& nums) {
int length = nums.size();
if (length == 1) {
return nums[0];
} else if (length == 2) {
return max(nums[0], nums[1]);
}
return max(robRange(nums, 0, length - 2), robRange(nums, 1, length - 1));
}
};
Java
class Solution {
public int rob(int[] nums) {
int length = nums.length;
if (length == 1) {
return nums[0];
} else if (length == 2) {
return Math.max(nums[0], nums[1]);
}
return Math.max(robRange(nums, 0, length - 2), robRange(nums, 1, length - 1));
}
public int robRange(int[] nums, int start, int end) {
int first = nums[start], second = Math.max(nums[start], nums[start + 1]);
for (int i = start + 2; i <= end; i++) {
int temp = second;
second = Math.max(first + nums[i], second);
first = temp;
}
return second;
}
}
C
int robRange(int* nums, int start, int end) {
int first = nums[start], second = fmax(nums[start], nums[start + 1]);
for (int i = start + 2; i <= end; i++) {
int temp = second;
second = fmax(first + nums[i], second);
first = temp;
}
return second;
}
int rob(int* nums, int numsSize) {
if (numsSize == 1) {
return nums[0];
} else if (numsSize == 2) {
return fmax(nums[0], nums[1]);
}
return fmax(robRange(nums, 0, numsSize - 2), robRange(nums, 1, numsSize - 1));
}
Python3
class Solution:
def rob(self, nums: List[int]) -> int:
def robRange(start: int, end: int) -> int:
first = nums[start]
second = max(nums[start], nums[start + 1])
for i in range(start + 2, end + 1):
first, second = second, max(first + nums[i], second)
return second
length = len(nums)
if length == 1:
return nums[0]
elif length == 2:
return max(nums[0], nums[1])
else:
return max(robRange(0, length - 2), robRange(1, length - 1))
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/house-robber-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
这是一个关于使用动态规划解决环形房屋盗窃问题的算法解析,通过分析示例和给出的官方题解,我们可以理解如何在不触发警报的情况下最大化窃取的金额。动态规划方法包括计算两段不同起始点的连续房屋所能偷窃的最大金额,并取两者之中的较大值作为最终结果。代码实现涵盖C++、Java和Python3等语言,实现了时间复杂度为O(n)和空间复杂度为O(1)的解决方案。

2118

被折叠的 条评论
为什么被折叠?



