LeetCode #198,213,337 打家劫舍三连 动态规划

LeetCode #198,213,337 打家劫舍三连

打家劫舍 I

你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警

给定一个代表每个房屋存放金额的非负整数数组,计算你在不触动警报装置的情况下,能够偷窃到的最高金额。

示例1:

输入: [1,2,3,1]
输出: 4
解释: 偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。偷窃到的最高金额 = 1 + 3 = 4 。

示例2:

输入: [2,7,9,3,1]
输出: 12
解释: 偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。偷窃到的最高金额 = 2 + 9 + 1 = 12 。

思路

  • 由于不可偷窃相邻的房屋,所以在当前位置 n n 房屋可盗窃的最大值要么是在 n 1 n-1 可盗窃的最大值,要么是在 n 2 n-2 可盗窃的最大值加当前房屋的值
  • 动态规划方程: d p [ n ] = m a x ( d p [ n 1 ] , d p [ n 2 ] + n ) dp[n] = max(dp[n-1], dp[n-2] + n)
  • n = 0 n = 0 时,没有房屋可偷,所以 d p [ 0 ] = 0 dp[0] = 0
  • n = 1 n = 1 时,只有一间房屋可偷,所以 d p [ 1 ] = n u m s [ 0 ] dp[1] = nums[0]
class Solution(object):
    def massage(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        if len(nums) == 0:
        	return 0;

        prev = 0
        curr = 0
		
		# 每次循环,计算到目前位置 “偷到当前房子为止的最大金额”
		for i in nums:
			# 循环开始时,curr 表示 dp[k-1],prev 表示 dp[k-2]
			# dp[k] = max(dp[k-1], dp[k-2] + i)
			prev, curr = curr, max(curr, prev + i)
			# 循环结束时,curr 表示 dp[k],prev 表示 dp[k-1]

		return curr
  • 时间复杂度: O ( n ) O(n)
  • 空间复杂度: O ( 1 ) O(1)

打家劫舍 II

你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都围成一圈,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警

给定一个代表每个房屋存放金额的非负整数数组,计算你在不触动警报装置的情况下,能够偷窃到的最高金额。

示例1:

输入: [2,3,2]
输出: 3
解释: 你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。

示例2:

输入: [1,2,3,1]
输出: 4
解释: 你可以先偷窃 1 号房屋(金额 = 1),然后偷窃 3 号房屋(金额 = 3)。偷窃到的最高金额 = 1 + 3 = 4 。

思路

打家劫舍的升级版,变成了环状,意味着第一间房子和最后一间房子之间只能选择一个偷,可以把这个问题简化成两个单排排列的房子取较大值的问题

  • 在不偷窃第一间房子的情况下(即 n u m s [ 1 : ] nums[1:] ),最大金额是 p 1 p1
  • 在不偷窃第二间房子的情况下(即 n u m s [ : n 1 ] nums[:n-1] ),最大金额是 p 2 p2
  • 偷窃最大金额: m a x ( p 1 , p 2 ) max(p1, p2)
class Solution(object):
    def rob(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        # 只有一间房子的时候没有环
        if(len(nums) == 1): return nums[0]

        def my_rob(nums):
            prev = 0
            curr = 0
            for i in nums:
                prev, curr = curr, max(curr, prev + i)
            return curr

        return max(my_rob(nums[1:]), my_rob(nums[:-1]))
  • 时间复杂度: O ( n ) O(n)
  • 空间复杂度: O ( 1 ) O(1)

打家劫舍 III

在上次打劫完一条街道之后和一圈房屋后,小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为“根”。 除了“根”之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果两个直接相连的房子在同一天晚上被打劫,房屋将自动报警。

计算在不触动警报的情况下,小偷一晚能够盗取的最高金额。

示例1:

输入: [3,2,3,null,3,null,1]

     3
    / \
   2   3
    \   \ 
     3   1

输出: 7 
解释: 小偷一晚能够盗取的最高金额 = 3 + 3 + 1 = 7.

示例2:

输入: [3,4,5,1,3,null,1]

     3
    / \
   4   5
  / \   \ 
 1   3   1

输出: 9
解释: 小偷一晚能够盗取的最高金额 = 4 + 5 = 9.

思路

树形DP问题,采用后根遍历,求最大独立子集。

class Solution(object):
    def rob(self, root):
        """
        :type root: TreeNode
        :rtype: int
        """
        def dfs(cur):
            # 如果不存在返回0
            if cur is None:
                return [0,0]
            left = dfs(cur.left)
            right = dfs(cur.right)

            # 根节点不打劫,儿子打劫或不打劫都可以,取最大值
            not_rob = max(left[0], left[1]) + max(right[0], right[1])
            # 根节点打劫,则儿子不能打劫
            rob = cur.val + left[0] + right[0]

            return [not_rob, rob]
        
        res = dfs(root)
        return max(res[0], res[1])

关联问题

LeetCode #面试题17.16 按摩师 动态规划

发布了67 篇原创文章 · 获赞 2 · 访问量 1377

猜你喜欢

转载自blog.csdn.net/weixin_42511320/article/details/105063842
今日推荐