文档讲解:代码随想录
视频讲解:代码随想录B站账号
状态:看了视频题解和文章解析后做出来了
class Solution:
def rob(self, nums: List[int]) -> int:
if not nums:
return 0
if len(nums) == 1:
return nums[0]
if len(nums) == 2:
return max(nums[0], nums[1])
dp = [0] * len(nums)
dp[0] = nums[0]
dp[1] = max(nums[0], nums[1])
for i in range(2, len(nums)):
dp[i] = max(nums[i] + dp[i-2], dp[i-1])
return dp[-1]
1. 确定dp数组以及下标的含义
dp[i] : 打家劫舍到下标为i的房子时,能够抢劫到的最大金额。
2. 确定递推公式
在达到一个房子的时候,有两种情况要考虑:
1. 是否抢劫这个房子,如果抢劫而不触发警报,就要在i-2的基础上加上当前房子的价值,即dp[i-2] + nums[i]
2. 不抢这个房子,那就要考虑dp[i-1]的房子作为直到当前房子的最大抢劫价值。这里不用考虑 i - 3或 i - 4是因为 i - 1因为这个递推公式一定比之前要大。
3. dp数组的初始化
从递推公式dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]);可以看出,递推公式的基础就是dp[0] 和 dp[1]
从dp[i]的定义上来讲,dp[0] 一定是 nums[0],dp[1]就是nums[0]和nums[1]的最大值即:dp[1] = max(nums[0], nums[1]);
4. 遍历顺序
从前向后遍历
5. dp数组举例
class Solution:
def rob(self, nums: List[int]) -> int:
if len(nums) < 3:
return max(nums)
# 情况二:不抢劫第一个房屋
result1 = self.robRange(nums[:-1])
# 情况三:不抢劫最后一个房屋
result2 = self.robRange(nums[1:])
return max(result1, result2)
def robRange(self, nums):
dp = [[0, 0] for _ in range(len(nums))]
dp[0][1] = nums[0]
for i in range(1, len(nums)):
dp[i][0] = max(dp[i - 1])
dp[i][1] = dp[i - 1][0] + nums[i]
return max(dp[-1])
与上一题不同,这一题的房屋连成了一个圈,所以要考虑两种情况。
1. 考虑第一个房屋,不考虑最后一个房屋
2. 考虑最后一个房屋,不考虑第一个房屋
分别在两种情况下得出最大的抢劫价值,返回它们中的更大者。
这里用到了二维dp数组,其中dp[i][0]的i表示第i个房屋,0就代表没有抢劫,1代表抢劫了。
如果不抢劫当前的房屋,那么取上一个房屋抢劫or不抢劫中的最大值。
如果抢劫当前的房屋,那么取上一个房屋不抢劫的值(因为警示灯)加上当前房屋的价值。
# 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: Optional[TreeNode]) -> int:
# dp数组(dp table)以及下标的含义:
# 1. 下标为 0 记录 **不偷该节点** 所得到的的最大金钱
# 2. 下标为 1 记录 **偷该节点** 所得到的的最大金钱
dp = self.traversal(root)
return max(dp)
# 要用后序遍历, 因为要通过递归函数的返回值来做下一步计算
def traversal(self, node):
# 递归终止条件,就是遇到了空节点,那肯定是不偷的
if not node:
return (0, 0)
left = self.traversal(node.left)
right = self.traversal(node.right)
# 不偷当前节点, 偷子节点
val_0 = max(left[0], left[1]) + max(right[0], right[1])
# 偷当前节点, 不偷子节点
val_1 = node.val + left[0] + right[0]
return (val_0, val_1)
二叉树遍历首先需要确定的是遍历顺序,这里用到后序遍历,是因为母节点需要两个子节点的返回值来确定它自己的值。
同样两种情况:
1. 偷当前节点,那就不能偷子节点。当前的.val加上左右子节点的[0],即没偷左右子节点的最大值。
2. 不偷当前节点,那就偷子节点。返回两个子节点能偷的最大值的最大值。
一路遍历上来,最后返回dp的最大值,也就是这个tuple两个值中的最大值。