198. 打家劫舍
自己的思路:
初始化两个dp数组,dp[i][0]表示不偷第i户,在0-i户可以偷到的最大金额,dp[i][1]表示偷i户在0-i户可以偷到的最大金额。
python
class Solution:
def rob(self, nums: List[int]) -> int:
n = len(nums)
dp = [[0] * 2 for _ in range(n)]
dp[0][1] = nums[0]
for i in range(1, n):
dp[i][0] = max(dp[i - 1][0], dp[i - 1][1])
dp[i][1] = dp[i - 1][0] + nums[i]
return max(dp[n - 1][0], dp[n - 1][1])
优化:
有一点臃肿,可以优化。dp[i][1]实际上跟dp[i-1][1]就没啥关系,直接把dp数组初始化成一维的就行了。
python
class Solution:
def rob(self, nums: List[int]) -> int:
n = len(nums)
if n == 1:
return nums[0]
if n == 2:
return max(nums[0], nums[1])
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 - 1], dp[i - 2] + nums[i])
return dp[n - 1]
更优化:
想起了斐波那契数列...
python
class Solution:
def rob(self, nums: List[int]) -> int:
n = len(nums)
if n == 1:
return nums[0]
if n == 2:
return max(nums[0], nums[1])
prev = nums[0]
cur = max(nums[0], nums[1])
for i in range(2, n):
prev, cur = cur, max(cur, prev + nums[i])
return cur
213. 打家劫舍 II
还是比较容易想到的,把环展成两个线性的,一个去头一个去尾即可。
python
class Solution:
def rob(self, nums: List[int]) -> int:
n = len(nums)
if n == 1:
return nums[0]
if n == 2:
return max(nums[0], nums[1])
def helper(n, 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 - 1], dp[i - 2] + nums[i])
return dp[n - 1]
return max(helper(n-1, nums[1:]), helper(n-1, nums[:-1]))
337. 打家劫舍 III
我的思路:
有点类似贪心的最后一题。
一顿操作AC了,中间遗漏了几种情况,修改后正确了。
python
class Solution:
def rob(self, root: Optional[TreeNode]) -> int:
def helper(root):
if not root.left and not root.right:
return 0, root.val
elif root.left and root.right:
left_without_self, left_with_self = helper(root.left)
right_without_self, right_with_self = helper(root.right)
return max(left_without_self + right_without_self, left_with_self + right_with_self, left_without_self + right_with_self, left_with_self + right_without_self), left_without_self + right_without_self + root.val
elif root.left and not root.right:
left_without_self, left_with_self = helper(root.left)
return max(left_with_self, left_without_self), left_without_self + root.val
elif root.right and not root.left:
right_without_self, right_with_self = helper(root.right)
return max(right_with_self, right_without_self), right_without_self + root.val
return max(helper(root))
优化:
终止条件从叶子节点改成空节点,可以将之后的情况全部统一起来。
python
class Solution:
def rob(self, root: Optional[TreeNode]) -> int:
def helper(root):
if not root:
return 0, 0
left = helper(root.left)
right = helper(root.right)
not_include = max(left) + max(right)
include = left[0] + right[0] + root.val
return not_include, include
return max(helper(root))
带备忘的递归:
python
class Solution:
def rob(self, root: Optional[TreeNode]) -> int:
memo = {}
def helper(node):
if not node:
return 0
if node in memo:
return memo[node]
val = node.val
# 如果偷当前节点,则不能偷其直接的左右子节点,但可以偷其孙子节点
if node.left:
val += helper(node.left.left) + helper(node.left.right)
if node.right:
val += helper(node.right.left) + helper(node.right.right)
# 不偷当前节点,可以偷其左右子节点
not_steal = helper(node.left) + helper(node.right)
# 对于当前节点,选择偷与不偷的最大值
result = max(val, not_steal)
memo[node] = result
return result
return helper(root)
今日总结:
自己写能AC,都能get到要点~~~精简的代码还是得看题解。