打家劫舍。

198. 打家劫舍

0-1,但不是背包。稍微有点tricky的地方是初始化,但是只要递推出来了初始化也就知道怎么做了。

对于每一家,判断:

因为依赖dp[i-2]和dp[i-1],所以肯定得至少先初始化两个元素。

def rob(self, nums: List[int]) -> int:
    if len(nums) == 1:
        return nums[0]
    dp = [nums[0],max(nums[0],nums[1])]+[0]*(len(nums)-2)
    for i in range(2, len(nums)):
        dp[i] = max(nums[i]+dp[i-2], dp[i-1])
    return dp[-1]   

213. 打家劫舍 II

成环的情况一般分类讨论:假设nums[0,1,2,3,4].

最后再max一下即可。

def rob(self, nums: List[int]) -> int:
    def acyclicRob(nums):
        if len(nums) == 1:
            return nums[0]
        dp = [nums[0],max(nums[0],nums[1])]+[0]*(len(nums)-2)
        for i in range(2, len(nums)):
            dp[i] = max(nums[i]+dp[i-2], dp[i-1])
        return dp[-1]
    if len(nums) == 1:
        return nums[0]
    return max(acyclicRob(nums[1:]), acyclicRob(nums[:-1]))

337. 打家劫舍 III

二叉树,第一反应就是recursion+memoization。

如果root被抢,那就不能抢root的孩子。使用一个函数参数来表示root的可被抢状态。

以下代码一气呵成:

def rob(self, root: TreeNode) -> int:
    memo = {}
    def robHelper(root, canRob):
        if not root:
            return 0
        if (root, canRob) in memo:
            return memo[(root, canRob)]
        res = 0
        if canRob:
            res = max(root.val+robHelper(root.left, False)+robHelper(root.right, False), robHelper(root.left, True)+robHelper(root.right, True))
        else:
            res = robHelper(root.left, True)+robHelper(root.right, True)
        memo[(root, canRob)] = res
        return res
    return robHelper(root, True)

虽然跑通了,但运行时间并不美好。

这题做的过程中一下让我想起了贪心5里面讲过的968.监控二叉树。当时我也是用了recursion并向函数传入状态参数,结果完美超时。当时的DP解法是后序遍历,从底向上计算状态。那么这题是不是也能这么搞一下呢?

设:

根据分析:

初始化:如果root是None,a=b=c=0完事。

def rob(self, root: TreeNode) -> int:
    def postorder(root):
        if not root:
            return 0,0,0
        la, lb, lc = postorder(root.left)
        ra, rb, rc = postorder(root.right)
        a = lb+rb+root.val
        b = lc+rc
        c = max(a, b)
        return a,b,c
    return postorder(root)[2]

时间复杂度从O(2^n)瞬间变成O(n),是不是觉得很棒棒呢。