Leetcode-D20-动态规划(二刷)-198. 打家劫舍&213. 打家劫舍 II&740. 删除并获得点数

198. 打家劫舍

1、第一次提交错了

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

在这里插入图片描述
2、在对dp【n】的定义上还需要明确。第一次定义dp【n】为偷完第n间房子的最大金额,应该改为偷到第n间房子为截至的最大金额;其次,在实际做的时候,dp【n】是还没有偷第n间房子的金额,与自己的定义不符。
3、dp【n】定义为截至到第n间房子为止能偷的最大金额。
首先,初始化0、1、2。
然后,写转移方程。要不从前2个过来;要不从前3个过来。根据dp定义,应该加上nums【n】
最后,从dp【n-2】和dp【n-1】中选最大值。

class Solution:
    def rob(self, nums: List[int]) -> int:
        n = len(nums)
        dp = [0]*n
        dp[0]=nums[0]
        if n==1:
            return dp[0]
        dp[1]= max(nums[0],nums[1])
        if n==2:
            return dp[1]
        dp[2]=max(nums[0]+nums[2],nums[1])
        if n==3:
            return dp[2]

        for i in range(3,n):
            dp[i] = max(dp[i-2]+nums[i],dp[i-3]+nums[i])
        return max(dp[n-2],dp[n-1])

在这里插入图片描述

4、学习一下解析

高票答案是把dp【n】定义为偷完前n个房子的max money。【我稍作改动,更好理解一点】
转移方程是dp[n] = max(dp[n-1],dp[n-2]+nums[n]),即要不不偷这个房间,要不偷这个房间,选最大值。
我写的是可以从哪里偷过来,我觉得两种思路都是可以的。

我写的和高票答案都可以压缩空间,但是我写的没那么直观啦,需要4个变量,因为需要保存n、n-1、n-2、n-3;高票只需要3个。

213. 打家劫舍 II

1、好问题及好解答
原问题本身说的是首尾不能连接《》两个房子不能同时偷《》(两个房子都不偷+只偷其中一个)《==》划分成两个问题可以满足原条件
在这里插入图片描述
2、尼玛的,一直报错气死了

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

        return max(max_money(nums[:N-1]),max_money(nums[1:]))


在这里插入图片描述

3、本来想写一个函数减少工作量,结果没考虑到返回值会出界的问题,需要先判断N==1。

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

        return max(max_money(nums[:N-1]),max_money(nums[1:]))


740. 删除并获得点数

1、一下子想不出来动态规划
2、牵扯到一个问题转换——将不规则排列,转换到规则的排列——用一个数组记录值为1,2,…,n的数量
3、

class Solution:
    def deleteAndEarn(self, nums: List[int]) -> int:
        N = max(nums)
        if N==0:
            return 0
        listed_nums = [0]*(N)
        for i in range(1,N+1):
            listed_nums[i-1] = nums.count(i)*i

        #问题转换为打家劫舍问题,不能取listed_nums中相邻的数值

        dp = [0]*(N)
        dp[0] = listed_nums[0]
        if N==1:
            return dp[0]
        dp[1] = max(listed_nums[1],listed_nums[0])
        if N==2:
            return dp[1]
        for i in range(2,N):
            dp[i] = max(dp[i-1],dp[i-2]+listed_nums[i])
        return max(dp[N-1],dp[N-2])


在这里插入图片描述
4、其实你刚开始已经意识到了,这个问题和打家劫舍问题很相似(不能取数值相邻的点),但是没有想到如何做数据处理,把原本不规则的数组,转换为不能取相邻值。具体转换方法就是,让数组中下标为i的地方储存原数组值为i+1的总和(储存值为0没有意义,所以从1开始储存;储存总和是因为一次直接加同一个数值的总和,所以储存一个数值的总和)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值