代码随想录算法训练营第四十六天 | 1049. 最后一块石头的重量 II,494. 目标和,474.一和零

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

题目链接:1049. 最后一块石头的重量 II
分析一

  1. 确定dp数组(dp table)以及下标的含义
    dp[j] 在容量为j的背包里放入物体能够达到的最大重量
  2. 确定递推公式
    dp[j] = max(dp[j], dp[j - weights[i]] + values[i])
  3. dp数组如何初始化
    dp[0] = 0
    for j in range(1, n + 1):
    if j >= weights[0]: dp[j] = values[0]
  4. 确定遍历顺序
    先遍历物体再遍历背包
  5. 举例推导dp数组
    stones = [2,7,4,1,8,1]
    capacity = int(round((2 + 7 + 4 + 1 + 8 + 1) / 2, 0)) = 12 (四舍五入)
    i = 0时: [0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]
    i = 1时: [0, 0, 2, 2, 2, 2, 2, 7, 7, 9, 9, 9, 9]
    i = 2时: [0, 0, 2, 2, 4, 4, 6, 7, 7, 9, 9, 11, 11]
    i = 3时: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
    i = 4时: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
    i = 5时: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
class Solution:
    def lastStoneWeightII(self, stones: List[int]) -> int:
        n = int(round(sum(stones) / 2, 0))
        dp = [0] * (n + 1)
        for j in range(1, n + 1):
            if j >= stones[0]:
                dp[j] = stones[0]

        for i in range(1, len(stones)):
        	# 注意为了让 j >= stones[i],最后一个遍历的元素应该是 stones[i]
            for j in range(n, stones[i] - 1, -1):
                dp[j] = max(dp[j], dp[j - stones[i]] + stones[i])

        return abs(sum(stones) - 2 * dp[-1])

494. 目标和

题目链接:494. 目标和

分析一

本题要如何转化为背包问题(使表达式结果为target)
既然为target,那么就一定有 left组合 - right组合 = target。
left + right = sum,而sum是固定的。right = sum - left
公式来了, left - (sum - left) = target 推导出 left = (target + sum)/2 。
target是固定的,sum是固定的,left就可以求出来。
此时问题就是在集合nums中找出和为left的组合。
dp 五部曲

  1. 确定dp数组(dp table)以及下标的含义
    这题是排列组合问题,所以dp定义为 填满容量为j的背包时有j种方法。
  2. 确定递推公式(不太懂这个递推公式)
    dp[j] += dp[j - nums[i]]; i = 0,1,…,n
  3. dp数组如何初始化
    如果数组[0] ,target = 0,那么 bagSize = (target + sum) / 2 = 0。 dp[0]也应该是1, 也就是说给数组里的元素 0 前面无论放加法还是减法,都是 1 种方法。
    其他位置初始化为0,由dp[0]推导而来
  4. 确定遍历顺序
    先遍历物体再遍历背包容量
  5. 举例推导dp数组(不太懂怎么推)
    nums = [1,1,1,1,1], target = 3
    n = (target + sum) / 2 = (3 + 5) / 2 = 4
    0 1 2 3 4
    nums[0] 1 1 0 0 0
    nums[1] 1 2 1 0 0
    nums[2] 1 3 3 1 0
    nums[3] 1 4 6 4 1
    nums[4] 1 5 10 10 5
class Solution:
    def findTargetSumWays(self, nums: List[int], target: int) -> int:
        s = sum(nums)
        # 注意要用绝对值,要同时考虑全正号和全负号的情况
        if (target + s) % 2 != 0 or (abs(target) > s):  
            return 0

        n = (target + sum(nums)) // 2
        dp = [1] + [0] * n

        for i in range(len(nums)):
            for j in range(n, nums[i] - 1, -1):
                dp[j] += dp[j - nums[i]]
        return dp[-1]

474.一和零

题目链接:474.一和零
分析一

问题转化
本题中strs 数组里的元素就是物品,每个物品都是一个!
而m 和 n相当于是一个背包,两个维度的背包。
其实就是01背包循环数组的升维版。
DP五部曲

  1. 确定dp数组(dp table)以及下标的含义
    dp[i][j] 表示用最多有 i 个 0 和 j 个 1 的子集最多有多少个(最大子集大小)
  2. 确定递推公式
    dp[i][j] = dp[i - zeroNum][j - oneNum] + 1 (没看懂这个)
    注:zeroNum 和 oneNum 表示前一个strs 0 的个数和 1 的个数
  3. dp数组如何初始化
    dp[0][0] = 0
  4. 确定遍历顺序:先遍历物品后遍历背包容量(之前循环数组说过原因)
  5. 举例推导dp数组
    strs = [“10”,“0001”,“111001”,“1”,“0”]
    m = 5, n = 3
    (1) 遍历物品1
    [0, 0, 0, 0]
    [0, 1, 1, 1]
    [0, 1, 1, 1]
    [0, 1, 1, 1]
    [0, 1, 1, 1]
    [0, 1, 1, 1]
    (2) 遍历物品2
    [0, 0, 0, 0]
    [0, 1, 1, 1]
    [0, 1, 1, 1]
    [0, 1, 1, 1]
    [0, 1, 2, 2]
    [0, 1, 2, 2]
    (3) 遍历物品3
    [0, 0, 0, 0]
    [0, 1, 1, 1]
    [0, 1, 1, 1]
    [0, 1, 1, 1]
    [0, 1, 2, 2]
    [0, 1, 2, 2]
    (4) 遍历物品4
    [0, 1, 1, 1]
    [0, 1, 2, 2]
    [0, 1, 2, 2]
    [0, 1, 2, 2]
    [0, 1, 2, 3]
    [0, 1, 2, 3]
    (5) 遍历物品5
    [0, 1, 1, 1]
    [1, 2, 2, 2]
    [1, 2, 3, 3]
    [1, 2, 3, 3]
    [1, 2, 3, 3]
    [1, 2, 3, 4]
class Solution:
    def findMaxForm(self, strs: List[str], m: int, n: int) -> int:
        # 初始化
        dp = [[0] * (n + 1) for _ in range(m + 1)]
        # 遍历字符
        for string in strs:
            zero_num, one_num = 0, 0
            for c in string:
                if c == "0":
                    zero_num += 1
                else:
                    one_num += 1
            
            for i in range(m, zero_num - 1, -1):
                for j in range(n, one_num - 1, -1):
                    dp[i][j] = max(dp[i][j], dp[i - zero_num][j - one_num] + 1)
            
        return dp[-1][-1]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值