代码随想录算法训练营第三十五天 |动态规划part03
0-1背包理论基础(一)
0-1背包 : n种物品,每种物品只有1个
完全背包 : n种物品,每种物品有无限多个
多重背包 : n种物品,每种物品各不相同
| 重量 | 价值 | |
|---|---|---|
| 0 | 1 | 15 |
| 1 | 3 | 20 |
| 2 | 4 | 30 |
背包最大总量为4,装满背包的话,最大价值能是多少。
暴力: 每个物品取或者不取,用回溯算法
一共有n个物品,时间复杂度是2的n次方。
标准解法:
-
确定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数组如何初始化
i 0 1 2 3 4 0 0 15 15 15 15 1 0 15 15 20 35 2 0 15 15 20 35 -
遍历顺序(可以颠倒)
-
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,用于存储背包在不同承重下的最大价值,初始值为0
for 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 分割等和子集
在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