leetcode——动态规划问题(打家劫舍)

打家劫舍1

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

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

思路分析
对于每一个房屋,要么偷要么不偷,
如果偷当前房屋,那么小偷就只能在下下一个房屋做偷与不偷的选择
如果不偷,那么小偷将在下一个房屋做偷与不偷的选择
状态为当前房屋对应索引能够偷到的最大金额
dp[i] = max(dp[i-1],dp[i-2] + 当前房屋金额)

class Solution:
    def rob(self, num: List[int]) -> int:
        n = len(num)
        if n == 0:return 0
        if n == 1:return num[0]
        for i in range(1,n):
            if i ==1:num[1] = max(num[0],num[1])
            else:
                num[i] = max(num[i-1],num[i-2]+num[i])
        return num[-1]

打家劫舍2

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

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

思路分析
我们可以利用第一题中的解决方案,由于房屋一个环,数组开头和结尾不能同时被偷,那么就只能有两种情况,要么偷开头不偷结尾,要么偷结尾不偷开头,所以可以将数组num分成num[:n-1]和num[1:],这两种情况中最大的金额就是我们要求最高金额。

class Solution:
    def rob(self, nums: List[int]) -> int:
        n = len(nums)
        if n == 0:return 0
        if n == 1:return nums[0]
        def rober(num):
            n = len(num)
            if n == 0:return 0
            if n == 1:return num[0]
            for i in range(1,n):
                if i ==1:num[1] = max(num[0],num[1])
                else:
                    num[i] = max(num[i-1],num[i-2]+num[i])
            return num[-1]
        return max(rober(nums[:n-1]),rober(nums[1:]))

打家劫舍3

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

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

思路分析
考虑每一个节点,小偷要么偷或者不偷,
用一个 res 数组记录小偷偷这个节点和不偷这个节点分别能得到的最高金额。
特殊情况:当前节点为空,res=【0,0】
如果node不为空:
1.小偷偷这个节点,那么小偷就不可以偷该节点的两个子节点
res[1]= node.val + node.left[0] + node.right[0]
2.小偷不偷这个节点,那么小偷可以选择偷其子节点或者不偷,
res[0]=max(node.left[0],node.left[1]) + max(node.right[0],node.right[1])

class Solution:
    def rob(self, root: TreeNode) -> int:
        def robber(root):
            res = [0,0]
            if not root:return res

            left = robber(root.left)
            right = robber(root.right)

            res[0] = max(left[0],left[1])+max(right[0],right[1])
            res[1] = root.val + left[0] + right[0]

            return res
        res = robber(root)
        return max(res[0],res[1])

按摩师

题目描述;一个有名的按摩师会收到源源不断的预约请求,每个预约都可以选择接或不接。在每次预约服务之间要有休息时间,因此她不能接受相邻的预约。给定一个预约请求序列,替按摩师找到最优的预约集合(总预约时间最长),返回总的分钟数。
输入: [1,2,3,1]
输出: 4
解释: 选择 1 号预约和 3 号预约,总时长 = 1 + 3 = 4。

基本思路:类似于打家劫舍问题,这么相邻时间不能预约,那么对于每个请求,有两个选择,接受或者不接受
d p [ i ] [ 0 ] dp[i][0] dp[i][0] 表示从0到i在i处不接受请求的总分钟数, d p [ i ] [ 1 ] dp[i][1] dp[i][1]表示在i处接受请求的总分钟数。

class Solution:
    def massage(self, nums: List[int]) -> int:
        n = len(nums)
        if n==1:return nums[0]
        if n==0:return 0
        dp = [[0]*2 for _ in range(n)]
        dp[0][0]=0
        dp[0][1]=nums[0]
        for i in range(1,n):
            dp[i][0] = max(dp[i-1][1],dp[i-1][0])
            dp[i][1] = dp[i-1][0]+nums[i]
        return max(dp[-1][0],dp[-1][1])
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值