你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金。这个地方所有的房屋都围成一圈,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你在不触动警报装置的情况下,今晚能够偷窃到的最高金额。
输入:
nums = [2, 3, 2]
输出:
3
解释:
你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2),因为它们是相邻的。
输入:
nums = [1, 2, 3, 1]
输出:
4
解释:
你可以先偷窃 1 号房屋(金额 = 1),然后偷窃 3 号房屋(金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4。
输入:
nums = [1, 2, 3]
输出:
3
这是一个打家劫舍问题的变种,由于房屋围成一个圆形,导致第一个房屋和最后一个房屋相邻。因此,我们需要考虑两种情况:
对于每种情况,我们可以使用动态规划来解决。具体的,假设 dp[i][0]
表示偷第 i
个房屋时的最大金额,dp[i][1]
表示不偷第 i
个房屋时的最大金额。我们使用状态转移方程来计算最大值。
最终结果为这两种情况的最大值。
from typing import List
class Solution:
def rob(self, nums: List[int]) -> int:
n = len(nums)
if n == 0: # 如果没有房屋,返回 0
return 0
if n == 1: # 如果只有一个房屋,返回该房屋金额
return nums[0]
# 分别处理两种情况
# 情况 1: 偷第一个房屋,不偷最后一个房屋
a = nums[0:n-1]
# 情况 2: 不偷第一个房屋,偷最后一个房屋
b = nums[1:n]
# 对情况 1 进行动态规划
dp1 = [[0, 0] for _ in range(n)]
for i in range(1, n):
dp1[i][0] = dp1[i-1][1] + a[i-1]
dp1[i][1] = max(dp1[i-1][0], dp1[i-1][1])
# 对情况 2 进行动态规划
dp2 = [[0, 0] for _ in range(n)]
for i in range(1, n):
dp2[i][0] = dp2[i-1][1] + b[i-1]
dp2[i][1] = max(dp2[i-1][0], dp2[i-1][1])
# 返回两种情况的最大值
return max(dp1[n-1][0], dp1[n-1][1], dp2[n-1][0], dp2[n-1][1])
O(n)
,其中 n
是房屋的数量。我们分别对两种情况进行动态规划,每次遍历一次 nums
数组,因此时间复杂度是线性的。O(n)
,我们使用两个 dp
数组来存储每种情况下的结果,因此空间复杂度是 O(n)
。如果我们想要优化空间复杂度,考虑到每次计算 dp[i]
时只需要用到 dp[i-1]
和 dp[i-2]
,可以将 dp
数组压缩为常数空间,利用两个变量来存储前两次的状态。
class Solution:
def rob(self, nums: List[int]) -> int:
n = len(nums)
if n == 0:
return 0
if n == 1:
return nums[0]
# 情况 1: 偷第一个房屋,不偷最后一个房屋
a = nums[0:n-1]
# 情况 2: 不偷第一个房屋,偷最后一个房屋
b = nums[1:n]
# 对情况 1 进行动态规划
prev2, prev1 = 0, 0
for num in a:
curr = max(prev1, prev2 + num)
prev2 = prev1
prev1 = curr
max1 = prev1
# 对情况 2 进行动态规划
prev2, prev1 = 0, 0
for num in b:
curr = max(prev1, prev2 + num)
prev2 = prev1
prev1 = curr
max2 = prev1
# 返回两种情况的最大值
return max(max1, max2)
nums = [2, 3, 2]
solution = Solution()
print(solution.rob(nums)) # 输出:3
nums = [1, 2, 3, 1]
solution = Solution()
print(solution.rob(nums)) # 输出:4
nums = [1, 2, 3]
solution = Solution()
print(solution.rob(nums)) # 输出:3
在本问题中,由于房屋是围成一圈的,因此我们需要将问题转化为两种情况来考虑:一种是偷第一个房屋,一种是偷最后一个房屋。通过动态规划来求解每种情况的最大偷窃金额,最终返回两种情况的最大值。
通过优化空间复杂度,我们能够进一步提高代码的效率。