代码随想录27期|Python|Day42|​动态规划|1049. 最后一块石头的重量 II|​494. 目标和 | 474.一和零

1049. 最后一块石头的重量 II

本题其实是分割成等大子集的变体。也就是说,尽可能将分成两组大小相等的石头,那么最后两组石头做差值,剩下的就是最小的。

不一样的地方在于

1、target的确定:上一题target确定前需要先判断是否是整数,但是这题不要先判定,因为不需要严格取到target。此时target为sum整除2后向下取整,取的是较小的值。

2、最后返回值的处理。不需要严格进行判定。

由于之前target取的是sum的一半整除之后比较小的一部分,所以最终dp结果计算出来的也是靠近较小值附近的。要求出剩余的石头质量,只要从sum中连续减掉两个dp下标target的值(两组对称)即可。

class Solution(object):
    def lastStoneWeightII(self, stones):
        """
        :type stones: List[int]
        :rtype: int
        """
        target = sum(stones) // 2 # 整除,且目标是总数小的那一堆
        dp = [0] * (target + 1)  # 初始化从下标0开始
        for i in range(len(stones)):
            for j in range(target, stones[i] - 1, -1):
                dp[j] = max(dp[j], dp[j-stones[i]]+stones[i])
        return sum(stones) - dp[target] * 2  # 去掉小的这一堆的最终值就是剩下的最小值

 494. 目标和

本题是dp爬楼梯问题的延伸。

根据上面题目分组的思路可以继续讲本题的数字进行分组,那么target就是两边相减的差值。可以用方程算出左边left分组的和:

        left - right = target

        left + right = sum

==>left = (sum + target) // 2

那么就可以变成一个背包问题(bagweight = left),也就是根据数字进行组合(此时只有+1或者+0,也就是0-1背包问题),使其能够达到left值。

1、dp数组的含义(一维)

dp[j]表示的就是填满容量为j的背包,一共存在的方法。注意不是求当前最大价值。

2、确定递归公式

dp[j]由前一项确定,是一个爬楼梯的问题。也就是,假设dp[j-1]...dp[0]存在,那么dp[j] = dp[j-1] ~ dp[0]的和。那么每一次都需要把前面的dp值加起来。所以递归公式是dp[j] += dp[j-1]。

3、初始化

dp[0]=1。这里和之前的不一样,因为之前dp代表价值的综合,没有物品,价值为0。但是现在给出[0]的数组,target=0的时候,存在1种方式,所以dp[0] = 1。

4、确定遍历顺序

遍历顺序同上一题,先遍历物品,再遍历背包(倒序)。

class Solution(object):
    def findTargetSumWays(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: int
        """
        # 排除target绝对值大于sum的情况
        if target > sum(nums) or target < -sum(nums):
            return 0
        # 排除找不到目标背包容量的情况
        elif (sum(nums)+target) % 2:
            return 0
        bagweight = (sum(nums)+target) / 2
        dp = [0] * (bagweight + 1)
        dp[0] = 1  # 初始化认为tar=0,只有一个元素是0的时候也存在1个方法
        for num in nums:
            for j in range(bagweight, num - 1, -1):
                dp[j] += dp[j - num]  # 递推公式
        return dp[-1]

本题还需要注意在一开始的时候加上两个判断:1、当target绝对值超过sum的时候;2、当left求出来不是整数的时候。最后返回dp的最后一个值。

474. 一和零

本题在原来背包问题的递推基础上加了一个维度,相当于对每一个物品,都有一个dp二维表格。行和列分别是0和1的容量。相当于多了一层for循环。

也就是说,如果考虑加上物品(字符串)的维度的话,是三维的dp表格。

1、dp数组的含义

dp[i][j]表示在当前1个数不超过i,0个数不超过j的最大子集大小;其中每一个字符串代表一个物品,其weight是两个维度,分别是0和1的个数,其value就是个数1。

2、确定递推条件

dp[i][j] = max(dp[i][j], dp[i-one_num][j-zero_num] + 1)其中,one_num和zero_num分别表示当前字符串的“重量”两个维度。更新操作为计数+1。

3、确定初始化

dp是计数的,所以全部初始化为0即可。

4、如何遍历

第一层对每一个物品(字符串)遍历;第二层对1的“背包容量”遍历;第三层对0对“背包容量”进行遍历。其中后面两个遍历顺序是等价的,都是从大到小遍历,临界条件是当前字符串的“重量”。

class Solution(object):
    def findMaxForm(self, strs, m, n):
        """
        :type strs: List[str]
        :type m: int
        :type n: int
        :rtype: int
        """
        dp = [[0] * (m + 1) for _ in range(n+1)]  # 创建二维数组,包含两个维度,行是‘1’的容量,列是‘0’的容量
        for str in strs:
            one_num = str.count('1')
            zero_num = str.count('0')
            for i in range(n, one_num-1, -1):  # 先遍历‘1’的容量
                for j in range(m, zero_num-1, -1):  # 再遍历‘0’的容量
                    dp[i][j] = max(dp[i][j], dp[i-one_num][j-zero_num] + 1)  # 更新数组,比较是取了之后+1个还是不取的数字大
        return dp[-1][-1]

Day42完结!!!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值