任務
198.打家劫舍
你是一個專業的小偷,計劃偷竊沿街的房屋。每間房內都藏有一定的現金,影響你偷竊的唯一制約因素就是相鄰的房屋裝有相互連通的防盜系統,如果兩間相鄰的房屋在同一晚上被小偷闖入,系統會自動報警。
給定一個代表每個房屋存放金額的非負整數陣列,計算你 不觸動警報裝置的情況下 ,一夜之內能夠偷竊到的最高金額。
思路
dp[i] 表示[0,i]的房屋所能偷竊到的最大金額,根據規則,dp[i] = max(dp[i-2]+nums[i],dp[i-1])),即選擇當前房間或者不選當前房間的最大值作為dp[i]。
class Solution:
def rob(self, nums: List[int]) -> int:
if len(nums) < 2: return nums[0]
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-2] + nums[i],dp[i-1])
return dp[len(nums)-1]
213. 打家劫舍 II
你是一個專業的小偷,計劃偷竊沿街的房屋,每間房內都藏有一定的現金。這個地方所有的房屋都 圍成一圈 ,這意味著第一個房屋和最後一個房屋是緊挨著的。同時,相鄰的房屋裝有相互連通的防盜系統,如果兩間相鄰的房屋在同一晚上被小偷闖入,系統會自動報警 。
給定一個代表每個房屋存放金額的非負整數陣列,計算你 在不觸動警報裝置的情況下 ,今晚能夠偷竊到的最高金額。
思路
考慮去掉末尾和去掉開頭兩種情況,取其中的較大者作為最大金額,其中包含了去掉開頭和結尾的情況。
class Solution:
def rob(self, nums: List[int]) -> int:
if len(nums) == 1 : return nums[0]
notConsiderLast = self.robA(0,len(nums)-1,nums)
notConsiderFirst = self.robA(1,len(nums),nums)
return max(notConsiderFirst,notConsiderLast)
def robA(self,lo,hi,nums):
if hi-lo == 1: return nums[lo]
dp = [0] * len(nums)
dp[lo] = nums[lo]
dp[lo+1] = max(nums[lo],nums[lo+1])
for i in range(lo+2,hi):
dp[i] = max(dp[i-2] + nums[i],dp[i-1])
return dp[hi-1]
337.打家劫舍 III
小偷又發現了一個新的可行竊的地區。這個地區只有一個入口,我們稱之為 root 。
除了 root 之外,每棟房子有且只有一個“父“房子與之相連。一番偵察之後,聰明的小偷意識到“這個地方的所有房屋的排列類似於一棵二叉樹”。 如果 兩個直接相連的房子在同一天晚上被打劫 ,房屋將自動報警。
給定二叉樹的 root 。返回 在不觸動警報的情況下 ,小偷能夠盜取的最高金額
思路
考慮樹形DP,每個節點向上返回的是選當前節點和不選當前節點的最大值列表。(這個比較難想到),但是想到後整體思路還是比較簡單
選當前節點的獲得的最大值等於 自己加上不選左右節點
不選當前節點的最大值等於 左右節點(選或不選)的最大值相加
# 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:
return max(self.robTree(root))
def robTree(self,node):
if not node: return [0,0]
left = self.robTree(node.left) #左邊收集和不收集分別最大的元素
right = self.robTree(node.right) #右邊收集和不收集分別最大的元素
#選當前node
val1 = node.val + left[1] + right[1]
#不選當前node
val2 = max(left[0],left[1]) + max(right[0],right[1])
return [val1,val2]