代码随想录算法训练营第三十九天 |动态规划part07
198 打家劫舍I
-
确定dp数组以及下标的含义
- dp[i] 考虑下标i(包含)偷的最大的金币dp[i]
-
递推公式
- 偷i dp[i-2] + num[i]
- 不偷i dp[i-1]
- dp[i] = max(dp[i-2] + num[i],dp[i-1])
-
dp数组如何初始化
- dp[0] = nums[0]
- dp[1] = max(nums[0],nums[1])
-
遍历顺序
- for i in range(2,n):
-
打印dp数组
- return dp[-1]
完整代码:
nums = [1,2,3,1]
# dp[i] 偷窃i个房屋的最大金额
n = len(nums)
dp = [0] * n
dp[0] = nums[0]
dp[1] = max(nums[0], nums[1])
for i in range(2,n):
dp[i] = max(dp[i-2] + nums[i],dp[i-1])
print(dp)
213 打家劫舍II
打家劫舍I是线性的数组,打家劫舍II变成了环形,首尾相连。
分成三种情况:
- 首尾皆不考虑,只考虑中间部分
- 考虑首,不考虑尾
- 考虑尾,不考虑首
二三情况其实是包含第一种情况的。
# nums = [2,3,2]
def f(nums,dp):
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(dp[i-1], dp[i-2] + nums[i])
return dp[-1]
# f(nums[:len(nums)-1],[])
# f(nums[1:],[])
if len(nums) == 1:
return nums[0]
elif len(nums) == 2:
return max(nums[0],nums[1])
else:
return max(f(nums[:len(nums)-1],[]),f(nums[1:],[]))
337 打家劫舍III
树形DP:在二叉树中进行状态转移。
每个节点是有两种状态,偷还是不偷。
所以dp[i] = 0 or 1 等于0的话就是不偷,等于1的话就是偷。
dp数组(dp table)以及下标的含义:
1. 下标为 0 记录 **不偷该节点** 所得到的的最大金钱
2. 下标为 1 记录 **偷该节点** 所得到的的最大金钱
所以需要定义一个函数,需要是后序遍历,用来遍历所有节点的情况。
if not node:
return (0,0)
然后进行左右遍历,
left = self....(node.right)
right = self....(node.left)
此时有两种情况,偷当前节点
val_0 = node.val + left[0] + right[0]
不偷当前节点
val_1 = max(left[0],left[1]) + max(right[0],right[1])
确定函数返回值
return (val_1,val_0)
完整代码如下
def rob(self, root: Optional[TreeNode]) -> int:
dp = self.travelsal(root)
return max(dp)
def travelsal(self,node):
if not node:
return (0,0)
left = self.travelsal(node.left)
right = self.travelsal(node.right)
# 偷当前节点
val_1 = node.val + left[0] + right[0]
# 不偷当前节点
val_0 = max(left[0],left[1]) + max(right[0],right[1])
return (val_0,val_1)
本人理解:(不知道正确与否)
进行后序遍历,然后每个节点都是需要向上一层进行状态转移。 如果偷的话,val_1 = node.val + left[0] + right[0] left[0] right[0] 均表示不偷子节点 这是因为我们每个节点都是带着之前节点的状态,所以需要加上孩子的最大值。 val_0 = max(left[0],left[1]) + max(right[0],right[1]) 因为不偷当前节点,所以左右孩子节点可偷可不偷,进行最大值的比较,决定偷不偷。max(left[0],left[1]) + max(right[0],right[1]) 如果不偷的话,那就是左孩子右孩子的最大值加起来,就是这个节点的值,然后传递给上一层。
另外,函数的返回值是一个集合,传递到根节点之后,根节点会有选与不选两种状态,所以直接进行两种状态的值进行比较,选择大的。
dp = (val_0,val_1) dp[0]表示不偷 dp[1]表示偷 之后的最大价值。