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

89 阅读5分钟

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

0-1背包理论基础(一)

0-1背包 : n种物品,每种物品只有1个

完全背包 : n种物品,每种物品有无限多个

多重背包 : n种物品,每种物品各不相同

重量价值
0115
1320
2430

背包最大总量为4,装满背包的话,最大价值能是多少。

暴力: 每个物品取或者不取,用回溯算法

一共有n个物品,时间复杂度是2的n次方。

image-20250113211624529.png

标准解法

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

    • dp [i] [j] : 下标为[0,i]之间的物品任取,放进容量为j的背包里
  • 递推公式

    • dp [i] [j]

      • 不放物品i dp [i-1] [j]
      • 放物品i dp [i-1] [j - weight[i]] (不放物品i,背包容量也减去了物品i时的最大价值) + value [i]
      • dp [i] [j] = max(dp[i-1] [j] , dp [i-1] [j - weight[i]]+ value [i])
  • dp数组如何初始化

    i01234
    0015151515
    1015152035
    2015152035
  • 遍历顺序(可以颠倒)

    • for (物品)

      for (背包)

  • 打印dp数组

n, bagweight = map(int, input().split())  # 从输入中读取两个整数,n表示物品的数量,bagweight表示背包的最大承重
​
weight = list(map(int, input().split()))  # 从输入中读取n个整数,表示每个物品的重量,并存储在列表weight中
value = list(map(int, input().split()))  # 从输入中读取n个整数,表示每个物品的价值,并存储在列表value中# 创建一个二维列表dp,用于存储动态规划的结果
# dp[i][j]表示前i个物品在背包承重为j时的最大价值
# 初始化为0,表示没有任何物品时,背包的价值为0
dp = [[0] * (bagweight + 1) for _ in range(n)]
​
# 初始化dp表的第一行,即只考虑第一个物品时的情况
# 如果背包的承重大于等于第一个物品的重量,则背包的价值为第一个物品的价值
for j in range(weight[0], bagweight + 1):
    dp[0][j] = value[0]
​
# 从第二个物品开始,逐个考虑每个物品
for i in range(1, n):
    # 遍历背包的承重范围
    for j in range(bagweight + 1):
        # 如果当前背包的承重小于当前物品的重量,则不能放入当前物品
        # 此时背包的最大价值等于前i-1个物品在承重为j时的最大价值
        if j < weight[i]:
            dp[i][j] = dp[i - 1][j]
        else:
            # 如果当前背包的承重大于等于当前物品的重量,则可以选择放入或不放入当前物品
            # 取放入和不放入当前物品时的最大价值
            dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i])
​
# 输出前n个物品在背包承重为bagweight时的最大价值
print(dp[n - 1][bagweight])

0-1背包理论基础(二)

用一维dp数组解决0-1背包

滚动数组

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

    • dp[j] 背包承重为j时的最大价值
  • 递推公式

    • dp[j] = max(dp[j],dp[j-weight[i]] + value[i]]
  • dp数组如何初始化

    • dp = [0] * (承重 + 1)
    • dp[0] = 0
  • 遍历顺序

    • for i in range(n):

      for j in range(bagweight,weight[i]-1,-1)

      倒序遍历,用来保证,每个物品只被添加过一次

  • 打印dp数组

n, bagweight = map(int, input().split())  # 从输入中读取两个整数,n表示物品的数量,bagweight表示背包的最大承重
​
weight = list(map(int, input().split()))  # 从输入中读取n个整数,表示每个物品的重量,并存储在列表weight中
value = list(map(int, input().split()))  # 从输入中读取n个整数,表示每个物品的价值,并存储在列表value中
​
dp = [0] * (bagweight + 1)  # 创建一个长度为bagweight+1的列表dp,用于存储背包在不同承重下的最大价值,初始值为0for i in range(n):  # 遍历每个物品,i表示当前物品的索引
    for j in range(bagweight, weight[i]-1, -1):  # 从背包的最大承重开始,倒序遍历到当前物品的重量
        dp[j] = max(dp[j], dp[j-weight[i]]+value[i])  # 更新dp[j],表示在承重为j时,选择当前物品或不选择当前物品的最大价值print(dp[bagweight])  # 输出背包在最大承重时的最大价值

416 分割等和子集

image-20250113215819730.png

在nums数组中,每个数组只能使用1次,看能不能装满容量为11的背包

直接变成0-1背包问题

target = sum(nums) // 2

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

    • dp[j] : 背包容量为j时的最大价值
  • 递推公式

    • dp[j] = max(dp[j],dp[j-num] + num)
  • dp数组如何初始化

    • dp = 0 * (target + 1)
  • 遍历顺序(倒序)

    • for num in nums:

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

  • 打印dp数组

nums = [1,5,11,5]  # 定义一个包含整数的列表 nums# 检查 nums 列表中所有元素的和是否为偶数
if sum(nums) % 2 != 0:  # 如果 nums 列表中所有元素的和除以 2 的余数不等于 0
    print(False)  # 输出 False,表示无法将列表分成两个和相等的子集# 计算目标值 target,即 nums 列表中所有元素和的一半
target = sum(nums) // 2  # 使用整数除法计算 target# 初始化动态规划数组 dp,长度为 target + 1,所有元素初始值为 0
dp = [0] * (target + 1)  # dp 数组用于存储每个可能的和的最大值# 遍历 nums 列表中的每个元素
for num in nums:  # 对于 nums 列表中的每个元素 num
    # 从 target 开始,倒序遍历到 num - 1
    for j in range(target, num-1, -1):  # j 从 target 开始,每次减 1,直到 j 小于 num
        # 更新 dp[j] 的值,取当前 dp[j] 和 dp[j-num] + num 中的较大值
        dp[j] = max(dp[j], dp[j-num] + num)  # dp[j] 表示在考虑当前 num 时,和为 j 的最大值# 检查 dp 数组的最后一个元素是否等于 target
print(dp[-1] == target)  # 如果 dp[-1] 等于 target,输出 True,表示可以将列表分成两个和相等的子集;否则输出 False