代码随想录算法训练营第三十六天 |动态规划part04

95 阅读3分钟

代码随想录算法训练营第三十六天 |动态规划part04

1049 最后一块石头的重量II

image-20250114200958311.png

思路:sum = 23 sum//2 = 11 尽量凑11,如果凑成和12相撞,剩1

  • 确定dp数组以及下标的含义

    • dp[j] : 装满容量为j的背包的最大重量是dp[j]
  • 递推公式

    • 0-1背包:dp[j] = max(dp[j],dp[j-weight[i]]+value[i])
    • dp[j] = max(dp[j],dp[j-stones[i]]+stones[i])
  • dp数组如何初始化

    • target = sum(stones) // 2
    • dp = [0] * (target + 1)
  • 遍历顺序

    • 如果使用一维dp数组,物品遍历的for循环放在外层,遍历背包的for循环放在内层,且内层for循环倒序遍历!
  • 打印dp数组

    • return (sum - dp[target]) - dp[target]
target = sum(stones) // 2
dp = [0] * (target + 1)
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) - 2 * dp[target]

16 目标和

image-20250114201009550.png

# left : + 之和
# right : - 之和
left + right = sum
left - right = target
right = sum - left
left - (sum - left) = target
left = (target + sum) / 2
如果 (target + sum) / 2 不是整数,那就说明找不出来 return 0
left : 背包容量
  • 确定dp数组以及下标的含义

    • dp[j] 装满容量为j的背包有dp[j]种方法
  • 递推公式

    • dp[j] += dp[j-nums[i]]
  • dp数组如何初始化

    • dp[0] = 1
  • 遍历顺序

    • for num in nums:

      for j in range(left, num - 1, -1):

      dp[j] += dp[j - num] # 其实就是放每个物品的情况做一个累加

  • 打印dp数组

    • return dp[target_sum] # 返回达到目标和的方案数
    if sum(nums) < target :
        return 0
    if (sum(nums) + target) % 2  == 1:
        return 0
    t = (sum(nums) + target) // 2
    if t < 0:
        return 0
    dp = [0] * (t + 1)
    dp[0] = 1
    for num in nums :
        for j in range(t,num-1,-1) :
            dp[j] += dp[j-num]
    return dp[-1]

17 一和零

image-20250114201017614.png

0-1背包:每个元素只能使用一次

  • 确定dp数组以及下标的含义

    • dp [i] [j] : 装满i个0 j个1 大小的背包最多背了dp [i] [j] 个物品(元素)
  • 递推公式

    • 0-1背包: dp[j] = max(dp[j],dp[j-weight[i]] + value[i])
    • 物品的重量: x 个 0 y 个 1
    • dp[i] [j] = max(dp [i-x] [j-y] + 1 , dp[i] [j])
  • dp数组如何初始化

    • dp [0] [0] = 0
    • 全都初始化成0 这样就不会影响dp[i] [j]
  • 遍历顺序

    • 先遍历物品,再遍历背包,背包要倒序遍历。
            for strr in strs:# 遍历物品
                x , y = 0 , 0
                for i in strr:
                    if i == 0:
                        x += 1
                    else :
                        y += 1
                for i in range(m,x-1,-1): # 遍历背包容量
                    for j in range(n,y-1,-1):
                        dp[i] [j] = max(dp [i-x] [j-y] + 1 , dp[i] [j])
  • 打印dp数组

    • return dp [m] [n]

完整代码:

    dp = [[0] * (n + 1) for _ in range(m + 1)]  # 创建二维动态规划数组,初始化为0
    for s in strs:  # 遍历物品
        zeroNum = s.count('0')  # 统计0的个数
        oneNum = len(s) - zeroNum  # 统计1的个数
        for i in range(m, zeroNum - 1, -1):  # 遍历背包容量且从后向前遍历
            for j in range(n, oneNum - 1, -1):
                dp[i][j] = max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1)  # 状态转移方程
    return dp[m][n]