CSDN话题挑战赛第2期
参赛话题:算法题解
打家劫舍 动态规划系列题目
三道 题目 动态规划的典型题目。
198. 打家劫舍
题目链接:198. 打家劫舍
题目大意:你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
例如:
输入:[1,2,3,1]
输出:4
解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
输入:[2,7,9,3,1]
输出:12
解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。
偷窃到的最高金额 = 2 + 9 + 1 = 12 。
解题思路: 开始时尝试 计算间隔数组的和 结果并不理想 这种思路不行。
使用动态数组不断地判断是否需要进行隔断累加,仍需要灵活地审题、答题。
class Solution:
def rob(self, nums: List[int]) -> int:
n = len(nums)
if n == 0: return 0
if n == 1: return nums[0]
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])
return dp[-1]
213. 打家劫舍 II
题目链接:213. 打家劫舍 II
题目大意:你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。
给定一个代表每个房屋存放金额的非负整数数组,计算你 在不触动警报装置的情况下 ,今晚能够偷窃到的最高金额
例如:
输入:nums = [2,3,2]
输出:3
解释:你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。
输入:nums = [1,2,3,1]
输出:4
解释:你可以先偷窃 1 号房屋(金额 = 1),然后偷窃 3 号房屋(金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
输入:nums = [1,2,3]
输出:3
解题思路: 198. 打家劫舍 的加强版
198的程序当作子函数,增加判断条件。
class Solution:
def rob(self, nums: List[int]) -> int:
def rob1(tmp: List[int]) -> int:
dp = [0]*len(tmp)
dp[0] = tmp[0]
dp[1] = max(tmp[0],tmp[1])
for i in range(2,len(tmp)):
# print(i)
dp[i] = max(dp[i-2]+tmp[i],dp[i-1])
# print(dp)
return dp[-1]
n = len(nums)
if n == 1: return nums[0]
elif n == 2: return max(nums[0],nums[1])
else:
return max(rob1(nums[:n-1]),rob1(nums[1:]))
337. 打家劫舍 III
题目链接:337. 打家劫舍 III
题目大意:小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为 root 。
除了 root 之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果 两个直接相连的房子在同一天晚上被打劫 ,房屋将自动报警。
给定二叉树的 root 。返回 在不触动警报的情况下 ,小偷能够盗取的最高金额 。
例如:
输入: root = [3,2,3,null,3,null,1]
输出: 7
解释: 小偷一晚能够盗取的最高金额 3 + 3 + 1 = 7
输入: root = [3,4,5,1,3,null,1]
输出: 9
解释: 小偷一晚能够盗取的最高金额 4 + 5 = 9
解题思路: 不能 按照以上的思路求取 需要根据 二叉树的结构进行 间隔选取。
# 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:
def _rob(node: Optional[TreeNode]) -> int:
# 不 rob
if node is None: return 0,0
L = _rob(node.left)
R = _rob(node.right)
# rob 取当前结点 左右子树不取
v1 = node.val + L[1] + R[1]
# not rob 不取当前结点 分别取左右子树中最大的值
v2 = max(L) + max(R)
return v1,v2
return max(_rob(root))
总结
这三道题特别有意思,专门记录一下,说不准以后就用到了。