你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你**在不触动警报装置的情况下,**能够偷窃到的最高金额。
示例 1:
输入: [1,2,3,1]
输出: 4
解释: 偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
示例 2:
输入: [2,7,9,3,1]
输出: 12
解释: 偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。
偷窃到的最高金额 = 2 + 9 + 1 = 12 。
解题思路
这个问题很简单,和之前的这个问题Leetcode 64:最小路径和(最详细的解法!!!) 很类似,但是比它容易。我们需要知道以index:0
开始的总金额,那么我们只要知道以index:2
开始的总金额,而我们需要知道以index:1
开始的总金额,那么我们只要知道以index:3
开始的总金额,以此类推下去。我们最后只要知道这些金额中的最大值即可。也就是
f[0] = max{nums[0]+f[2], nums[1]+f(3), nums[2]+f(4)...}
基于这种思想,可以写出下面的代码
class Solution:
def rob(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
result = 0
if not nums:
return result
for i in range(len(nums)):
result = max(result, nums[i] + self.rob(nums[i+2:]))
return result
但是这样做存在着大量的重复运算(在哪呢?)。我们可以通过记忆化搜索的方式来优化上面的问题。
class Solution:
def rob(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
self.len_nums = len(nums)
mem = [None for _ in range(self.len_nums)]
return self._rob(nums, 0, mem)
def _rob(self, nums, index, mem):
if index >= self.len_nums:
return 0
if mem[index] != None:
return mem[index]
result = 0
for i in range(index, self.len_nums):
result = max(result, nums[i] + self._rob(nums, i + 2, mem))
mem[index] = result
return result
同样这个问题我们也可以像之前处理Leetcode 64:最小路径和(最详细的解法!!!) 一样,反过来思考,同样这也是动态规划的思想。我们从最后一个元素开始考虑问题,我们每次更新的mem
,只要比较原先mem[i]
的值和mem[i + 2]
包含nums[i]
后的值哪个更大。比如说对于9
2 7 9 3 1
|->
<-
我们要思考原先9
之后的不相邻元素和与以9
作为起始点的不相邻元素和哪个更大。也就是
for i in range(3, 6):
mem[3] = max(mem[3], nums[i] + mem[i + 2])
基于此,我们很容易写出下面代码
class Solution:
def rob(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
if not nums:
return 0
len_nums = len(nums)
mem = [0 for _ in range(len_nums)]
mem[-1] = nums[-1]
for i in range(len_nums - 1, -1, -1):
for j in range(i, len_nums):
if j + 2 < len_nums:
mem[i] = max(mem[i], mem[j + 2] + nums[j])
else:
mem[i] = max(mem[i], nums[j])
return mem[0]
我们前面在处理这个问题的时候,默认函数定义是处理[x,n]
这个区间内的值,我们也可将函数定义为[0,x]
之间的值。我们只要对上面的代码稍加修改即可
class Solution:
def rob(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
if not nums:
return 0
len_nums = len(nums)
mem = [0 for _ in range(len_nums)]
mem[0] = nums[0]
for i in range(0, len_nums):
for j in range(0, i + 1):
if j - 2 >= 0:
mem[i] = max(mem[i], mem[j - 2] + nums[j])
else:
mem[i] = max(mem[i], nums[j])
return mem[-1]
实际上我们可以写出更加简洁的代码,我们只需要遍历一遍nums
就够了。但是这里的思路和之前不同,对与index:i
的数来说,它的最大数和,无非就是f[i-1]
和f[i-2]+nums[i]
中的一个,所以我们可以得到这样的方程
f[i] = max(f[i-1], f[i-2]+nums[i])
基于此,我们写出下面的代码
class Solution:
def rob(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
if not nums:
return 0
len_nums = len(nums)
if len_nums == 1:
return nums[0]
mem = [0 for _ in range(len_nums)]
mem[0], mem[1] = nums[0], max(nums[0], nums[1])
for j in range(2, len_nums):
mem[j] = max(mem[j - 1], mem[j - 2] + nums[j])
return mem[-1]
我们可以继续将代码简化为下面这个版本,思想同上面一样
class Solution:
def rob(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
pre, cur = 0, 0
for i in nums:
pre, cur = cur, max(pre + i, cur)
return cur
非常的简洁,非常完美的解法。
我将该问题的其他语言版本添加到了我的GitHub Leetcode
如有问题,希望大家指出!!!