LeetCode 198. 打家劫舍
一排房屋,某房屋内的金额数为nums[i]
,相邻房屋内的钱不能被同时取出,求最多能取出多少钱
nums=[1,1,3,4],返回5(选择取出1+4=5)
- dp[i]代表对于0~i号房屋,能取走的最大金额
- 依次考虑每个房屋,显然每次只有两种选择:
dp[i] = max(dp[i - 1], dp[i - 2] + nums[i])
若要取出当前房屋的钱,则不能拿取前一个房屋的钱
若不取出当前房屋的钱,则可以拿取前一个房屋的钱(当然,前一个房屋同样可以选择拿或不拿)
class Solution:
def rob(self, nums: List[int]) -> int:
"""房屋线性排列,只能拿取不相邻房间中的钱"""
L = len(nums)
if L <= 2:
return max(nums)
# dp[i]代表对于0~i号房屋,能取走的最大金额
dp = [0 for _ in range(len(nums))]
# base case
dp[0] = nums[0]
dp[1] = max(nums[0], nums[1])
for i in range(2, L):
dp[i] = max(dp[i - 1], dp[i - 2] + nums[i]) # 拿或者不拿
return dp[-1]
LeetCode 213. 打家劫舍 II
房屋变为环形排列,要求同上
nums = [2,3,2],返回3
- 房屋环形排列,只需要解决首尾连接处的特殊情况即可
如果要拿首部房屋内的钱,则不能拿尾部房屋内的钱
如果不拿首部房屋内的钱,则可以拿尾部房屋内的钱(当然,尾部房屋同样能选择拿或不拿) - 这样,问题被拆为两部分:
对于0~N-2
号房屋(视为线性排列),求能取出的最大金额
对于1~N-1
号房屋(视为线性排列),求能取出的最大金额
套用上一题的代码即可求解
class Solution:
def rob(self, nums: List[int]) -> int:
"""房屋环形排列,只能拿取不相邻房间中的钱"""
def rob_block(nums: List[int]) -> int:
"""房屋线性排列,只能拿取不相邻房间中的钱"""
L = len(nums)
if L <= 2:
return max(nums)
# dp[i]代表对于0~i号房屋,能取走的最大金额
dp = [0 for _ in range(len(nums))]
# base case
dp[0] = nums[0]
dp[1] = max(nums[0], nums[1])
for i in range(2, L):
dp[i] = max(dp[i - 1], dp[i - 2] + nums[i]) # 拿或者不拿
return dp[-1]
# 首尾房屋不能同时拿钱,分两种情况处理:要么在[0,n-2]中的房屋中拿钱,要么在[1,n-1]中的房屋中拿钱
N = len(nums)
if N == 1:
return nums[0]
return max(rob_block(nums[0:N - 1]), rob_block(nums[1:N]))
LeetCode 337. 打家劫舍 III
房屋坐落在一棵二叉树的各个结点上,其余要求同上
- 每个房屋仍然是两种选择:
拿现金,则不能拿相邻节点的现金
不拿现金,则可以拿相邻节点的现金(相邻节点同样在拿和不拿中做出最优选择) - 利用二叉树的遍历写出递归解法,在此基础上用备忘录优化递归解法即可
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def rob(self, root: TreeNode) -> int:
"""房屋树状排列,只能拿取不相邻房间中的钱"""
memo = {}
def dp(node):
"""dp(Treenode)代表某个节点及其子树的最大拿取金额"""
nonlocal memo
if not node:
return 0
if node not in memo:
# 从当前房屋拿现金
select = node.val
if node.left:
select += dp(node.left.left) + dp(node.left.right)
if node.right:
select += dp(node.right.left) + dp(node.right.right)
# 不拿现金
notSelect = dp(node.left) + dp(node.right)
# 选择更大的那一个
memo[node] = max(select, notSelect)
return memo[node]
return dp(root)