198.打家劫舍
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
明显是动态规划类题目,直接入手做很容易碰壁。我一开始就想着:找到最大值的那个元素,并以此为中心找到它相邻的数值组成最高金额。但其实是有很多漏洞的,无法通过所有测试用例。
因此,就需要动态规划。因为每一步都是有规律的,因此可以通过DP方程。利用数组来记录每一步的最高金额。但弊端就是内存消耗较大。
我的代码:
class Solution(object):
def rob(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
if not nums:
return 0
if len(nums) == 1:
return nums[0]
dp = [0 for i in range(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 max(dp)
213.打家劫舍 II
你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。
给定一个代表每个房屋存放金额的非负整数数组,计算你 在不触动警报装置的情况下 ,今晚能够偷窃到的最高金额。
这个题就是将列表看成环形,只需要将一个列表分为两个表(即第一个与最后一个不能在一个表里)。其他的部分与上一题一样。
class Solution(object):
def rob(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
if len(nums) == 1:
return nums[0]
elif len(nums) == 2:
return max(nums[0], nums[1])
p1 = nums[0: -1]
p2 = nums[1: ]
dp1 = [0 for i in range(len(nums))]
dp2 = [0 for i in range(len(nums))]
dp1[0] = p1[0]
dp1[1] = max(dp1[0], p1[1])
dp2[0] = p2[0]
dp2[1] = max(dp2[0], p2[1])
for i in range(2, len(nums)-1):
dp1[i] = max(dp1[i-1], dp1[i-2] + p1[i])
dp2[i] = max(dp2[i-1], dp2[i-2] + p2[i])
return max(max(dp1), max(dp2))
337.打家劫舍 III
小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为 root 。
除了 root 之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果 两个直接相连的房子在同一天晚上被打劫 ,房屋将自动报警。
给定二叉树的 root 。返回 在不触动警报的情况下 ,小偷能够盗取的最高金额 。
起初以为这个题只是简单的以层序遍历为基础的变种题。于是就将DP与层序遍历结合。
错误代码:
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def rob(self, root):
"""
:type root: TreeNode
:rtype: int
"""
if not root:
return 0
def depth(root):
if not root:
return 0
dl = depth(root.left)
dr = depth(root.right)
return max(dl, dr) + 1
if depth(root) == 1:
return root.val
if depth(root) == 2:
if root.left and root.right:
left_right = root.left.val + root.right.val
return max(left_right, root.val)
elif root.left:
return max(root.left.val, root.val)
elif root.right:
return max(root.right.val, root.val)
# 以上为特殊用例处理
dp = [0 for i in range(depth(root))]
dp[0] = root.val
lay_value_1 = []
queue = []
if root.left:
queue.append(root.left)
lay_value_1.append(root.left.val)
if root.right:
queue.append(root.right)
lay_value_1.append(root.right.val)
dp[1] = max(root.val, sum(lay_value_1))
i = 2
while queue and i < depth(root):
lay = []
lay_value = []
for node in queue:
if node.left:
lay_value.append(node.left.val)
lay.append(node.left)
if node.right:
lay_value.append(node.right.val)
lay.append(node.right)
queue = lay
dp[i] = max(dp[i-1], dp[i-2] + sum(lay_value))
i += 1
return max(dp)
但这个代码无法通过所有测试用例,因为需要考虑左右子树高度不同且节点值相差较大的情况。简单的将每一层的最优解存入dp[i]而不考虑树的整体结构的话是不行的。后来发现这也是很多人遇到的问题,都在遇到该测试用例的时候才发现自己代码的问题所在。
通过查阅相关资料及答案,选择了动态规划方法,与dp有类似之处。
将整棵树分为若干个子节点进行求解,每个子节点视为两个子问题,都有两个情况:抢该节点或不抢该节点。最后通过递归来得到根节点的res矩阵值。
我的代码:
class Solution(object):
def rob(self, root):
"""
:type root: TreeNode
:rtype: int
"""
def dp(root):
if not root: # 根节点为空
return [0, 0] # 返回0
res = [0, 0] # 初始化数组
"""
二维数组
res[0]代表不抢劫当前节点的最大值
res[1]代表抢劫当前节点的最大值
"""
left = dp(root.left) # 求左子树
right = dp(root.right) # 求右子树
res[0] = max(left[0], left[1]) + max(right[0], right[1]) # 若不抢根节点能得到的最大值
res[1] = root.val + left[0] + right[0] # 若抢根节点能得到的最大值
return res # 返回根节点的数值
res = dp(root)
return max(res[0], res[1])