566 字
3 分钟
代码随想录算法训练营第三十四天 | 动态规划 Part07
198. 打家劫舍
思路分析:
- 使用递归 + 记忆化搜索(自顶向下)
dp[i]
表示从第i
间房子开始偷窃所能获得的最大金额
Python代码:
class Solution: def rob(self, nums: List[int]) -> int: # 初始化记忆数组,避免重复计算 self.memo = [-1] * len(nums) return self.dp(nums, 0)
def dp(self, nums, start): # 递归终止条件:超出数组范围 if start >= len(nums): return 0
# 如果已经计算过,直接返回记忆值 if self.memo[start] != -1: return self.memo[start]
# 决策:偷 or 不偷当前房子 res = max( self.dp(nums, start + 1), # 不偷当前房子 nums[start] + self.dp(nums, start + 2) # 偷当前房子,然后跳过下一个 )
self.memo[start] = res return res
213. 打家劫舍 II
-
环形问题 ⇒ 不能同时选择第一个和最后一个
-
转化为两个线性子问题:
- 情况一:不偷最后一个房子(范围:
[0, n-2]
) - 情况二:不偷第一个房子(范围:
[1, n-1]
)
- 情况一:不偷最后一个房子(范围:
-
两者取最大值即可。
class Solution: def rob(self, nums: List[int]) -> int: n = len(nums) if n == 1: return nums[0]
# 分两种情况:不包含最后一个 和 不包含第一个 memo1 = [-1] * n memo2 = [-1] * n
return max( self.dp(nums, 0, n - 2, memo1), # 偷[0, n-2] self.dp(nums, 1, n - 1, memo2) # 偷[1, n-1] )
def dp(self, nums, start, end, memo): # 边界条件 if start > end: return 0
# 返回记忆值 if memo[start] != -1: return memo[start]
# 决策:偷 or 不偷当前房子 res = max( self.dp(nums, start + 1, end, memo), # 不偷当前房子 nums[start] + self.dp(nums, start + 2, end, memo) # 偷当前房子 )
memo[start] = res return res
337. 打家劫舍 III
LeetCode 337. House Robber III
- 对每个节点做决策:偷 or 不偷
- 返回值为一个二元组
(不偷该节点的最大值, 偷该节点的最大值)
- 采用后序遍历递归 + 动态规划(树形DP)
class Solution: def rob(self, root: Optional[TreeNode]) -> int: # 返回值是一个元组:(不偷当前节点, 偷当前节点) return max(self.dfs(root))
def dfs(self, node): # 空节点返回 (0, 0) if not node: return (0, 0)
# 后序遍历左右子树 left = self.dfs(node.left) right = self.dfs(node.right)
# 不偷当前节点:左右子节点可偷可不偷,取最大 not_rob = max(left) + max(right)
# 偷当前节点:不能偷左右子节点 rob = node.val + left[0] + right[0]
return (not_rob, rob)
代码随想录算法训练营第三十四天 | 动态规划 Part07
https://fuwari.vercel.app/posts/code_musings_day_thirty-four/