01背包
有n件物品和一个最多能背重量为w的背包。第i件物品的重量是weight[i],得到的价值是value[i]
重点:每个物品只能用一次
暴力的时间复杂度: 动规:
二维dp数组
动规五部曲
- 确定dp数组以及下标的含义
dp[i][j]i:物品 j:背包容量 - 递推公式
dp[i][j] = max(dp[i-1][j-weight[i]]+value[i], dp[i-1][j])注意都是i-1因为是从上一状态得到的 - 初始化 三种情况
dp[i][0]=0dp[0][j]=value[0]else:dp[i][j]=0 - 确定遍历顺序 先遍历物品 再遍历背包 其实只需要左上角的状态
- 举例验证
def test_2_wei_bag_problem1(bag_size, weight, value) -> int:
rows, cols = len(weight), bag_size + 1
dp = [[0 for _ in range(cols)] for _ in range(rows)]
# 初始化dp数组.
for i in range(rows):
dp[i][0] = 0
first_item_weight, first_item_value = weight[0], value[0]
for j in range(1, cols):
if first_item_weight <= j:
dp[0][j] = first_item_value
# 更新dp数组: 先遍历物品, 再遍历背包.
# len(weight), i.e. num of things
for i in range(1, len(weight)):
cur_weight, cur_val = weight[i], value[i]
for j in range(1, cols):
# 要判断背包能不能装下当前物品
if cur_weight > j: # 说明背包装不下当前物品.
dp[i][j] = dp[i - 1][j] # 所以不装当前物品.
else:
# 定义dp数组: dp[i][j] 前i个物品里,放进容量为j的背包,价值总和最大是多少。
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - cur_weight]+ cur_val)
print(dp)
一维dp数组
背包问题,状态可以压缩
上一层可以重复利用,直接拷贝到当前层
即,把dp[i - 1]那一层拷贝到dp[i]上:dp[i][j] = max(dp[i][j], dp[i][j - weight[i]] + value[i])
可以只遍历背包,通过背包重量是否改变说明是否放入
递推公式为:
dp[j] = max(dp[j], dp[j - weight[i]] + value[i])
推导的时候一定是取价值最大的数,所以需要从后往前遍历赋值
倒序遍历是为了保证物品i只被放入一次
而对于对于二维dp,dp[i][j]都是通过上一层即dp[i - 1][j]计算而来,本层的dp[i][j]并不会被覆盖
def test_1_wei_bag_problem():
weight = [1, 3, 4]
value = [15, 20, 30]
bag_weight = 4
# 初始化: 全为0
dp = [0] * (bag_weight + 1)
# 先遍历物品, 再遍历背包容量
for i in range(len(weight)):
# 背包从最大价值开始,但j - weight[i]需要大于等于0 因此注意遍历范围
for j in range(bag_weight, weight[i] - 1, -1):
# 递归公式
dp[j] = max(dp[j], dp[j - weight[i]] + value[i])
print(dp)
一维dp数组的遍历范围、遍历顺序、内外层顺序很重要
416. 分割等和子集
def canPartition(self, nums: List[int]) -> bool:
# 分割两个背包 背包容量 sum(nums)//2
# 物品 len(nums)
n = len(nums)
s = sum(nums)
if s % 2 != 0:
return False
weight = s // 2
# 二维dp
# dp = [[0] * (weight+1) for i in range(n)]
# for j in range(weight+1):
# dp[0][j] = nums[0]
# for i in range(n):
# for j in range(weight+1):
# if j >= nums[i]:
# dp[i][j] = max(dp[i-1][j],dp[i-1][j-nums[i]]+nums[i])
# else:
# dp[i][j] = dp[i-1][j]
# 一维dp
dp = [0] * (weight+1)
for i in range(n):
for j in range(weight,nums[i]-1,-1):
dp[j] = max(dp[j],dp[j-nums[i]]+nums[i])
# print(dp)
return (dp[-1]==weight)