198. 打家劫舍
思路:
dp定义: dp(i)表示从i个房子开始抢到的最大值
状态转移方程:dp(i) = max(dp(i+1), nums[i]+dp(i+2)) # 不抢,抢下一家;抢当前,之后抢i+2
base case:走过了最后一间房子,n>=len(nums),没得抢了,金额为0
class Solution:
def rob(self, nums: List[int]) -> int:
# 处理base case 0 1 2 的情况
n = len(nums)
if n == 0:
return 0
elif n==1:
return nums[0]
elif n==2:
return max(nums[0], nums[1])
# dp[i]表示抢到第i个房子的最大价值
dp = [0] * n
dp[0] = nums[0]
dp[1] = max(nums[0], nums[1])
# 迭代1~ n-1
for i in range(2, n):
dp[i] = max(nums[i] + dp[i-2], dp[i-1])
return dp[n-1]
class Solution:
def rob(self, nums: List[int]) -> int:
# 处理base case 0 1 2 的情况
n = len(nums)
if n == 0:
return 0
elif n==1:
return nums[0]
prevMax = 0 # 上一个房屋的最大金额 (相当于n-2)
curMax = 0 # 当前房屋的最大金额 (相当于n-1)
for num in nums:
temp = curMax
curMax = max(curMax, prevMax+num)
prevMax = temp
return curMax
213. 打家劫舍 II
思路:
prev cur next(num)
当道next时有两种情况:
1 抢num,那就要加n-2的价值: prev+num
2 不抢num,那就上一个cur的
curMax = max(curMax, prevMax+num)
class Solution:
def rob(self, nums: List[int]) -> int:
# base case
n = len(nums)
if n == 0:
return 0
elif n == 1:
return nums[0]
# 不抢最后一个
prevMax = 0 # 上一个房屋的最大金额
curMax = 0 # 当前房屋的最大金额
for num in nums[:-1]:
temp = curMax
curMax = max(curMax, prevMax + num)
prevMax = temp
result1 = curMax
# 不抢开头
prevMax = 0
curMax = 0
for num in nums[1:]:
temp = curMax
curMax = max(curMax, prevMax + num)
prevMax = temp
result2 = curMax
return max(result1, result2)
337. 打家劫舍 III
思路1: 暴力递推(后序)
1 偷父节点,接着偷孙子节点
2 不偷父节点,考虑偷孩子节点
然后求两种方法的最大值
class Solution:
def rob(self, root: Optional[TreeNode]) -> int:
if not root:
return 0
if not root.left and not root.right:
return root.val # 叶子节点
# 偷父亲节点
val1 = root.val
if root.left:
val1 += self.rob(root.left.left) + self.rob(root.left.right)
if root.right:
val1 += self.rob(root.right.left) + self.rob(root.right.right)
# 不偷
val2 = self.rob(root.left) + self.rob(root.right)
return max(val1, val2)
思路2: 带备忘录的递推
用memo来记录已经算过的节点,避免反复计算某个节点。
class Solution:
def rob(self, root: Optional[TreeNode]) -> int:
self.memo = {} # 记录节点到这个节点最大的盗窃的数额
return self.rob_helper(root)
def rob_helper(self, root: Optional[TreeNode]) -> int:
if not root:
return 0
if not root.left and not root.right:
return root.val # 叶子节点
# 判断是否在memo中
if root in self.memo:
return self.memo[root]
# 偷父亲节点
val1 = root.val
if root.left:
val1 += self.rob_helper(root.left.left) + self.rob_helper(root.left.right) # 注意调用rob_helper!
if root.right:
val1 += self.rob_helper(root.right.left) + self.rob_helper(root.right.right)
# 不偷
val2 = self.rob_helper(root.left) + self.rob_helper(root.right)
self.memo[root] = max(val1, val2)
return self.memo[root]
思路3: 树形dp
树形dp
dp数字定义:<a,b> a表示不偷当前节点的子树最大盗窃金额;b表示偷当前节点的最大金额
robTree:某个节点root为根的子树的 最大盗窃金额,返回<a,b>
初始化:空时候返回0,0
后序位置迭代
class Solution:
def rob(self, root: Optional[TreeNode]) -> int:
result = self.robTree(root)
return max(result)
# 后序遍历, 要通过递归函数的返回值来做下一步计算
def robTree(self, root: Optional[TreeNode]) -> int:
if not root:
return [0,0] # 偷的最大值,不偷当前的最大值
# 先计算左右的值
left = self.robTree(root.left)
right = self.robTree(root.right)
# 不偷当前
val1 = max(left[0], left[1]) + max(right[0], right[1])
# 偷当前节点
val2 = root.val + left[0] + right[0]
return (val1, val2)