D42|背包问题理论篇

背包类型

重点掌握01背包和完全背包,尤其是01背包
在这里插入图片描述

01背包问题 二维

前提:通过图形帮助理解递推公式和想象一维时的情况

  1. 确定dp数组以及下标的含义
    dp[i][j] 表示从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少。

  2. 确定递推公式
    从两个方向推出来dp[i][j],分为不放物品i和放物品i去考虑:
    dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i])

  3. dp数组如何初始化
    在这里插入图片描述

for (int j = 0 ; j < weight[0]; j++) {  // 当然这一步,如果把dp数组预先初始化为0了,这一步就可以省略,但很多同学应该没有想清楚这一点。
    dp[0][j] = 0;
}
// 正序遍历
for (int j = weight[0]; j <= bagweight; j++) {
    dp[0][j] = value[0];
}
  1. 确定遍历顺序
    此时由于二维数组,已事先进行了dp数组初始化,则不管是先遍历物品还是遍历背包容量都不会影响dp[i][j]的值

模板

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数组: 先遍历物品, 再遍历背包. 
	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)


if __name__ == "__main__": 
	bag_size = 4
	weight = [1, 3, 4]
	value = [15, 20, 30]
	test_2_wei_bag_problem1(bag_size, weight, value)

01背包问题(滚动数组)

主要讲dp定义+递推公式+遍历顺序

  1. dp定义
    dp[j]表示:容量为j的背包,所背的物品价值可以最大为dp[j]

  2. 递推公式
    dp[j] = max(dp[j], dp[j - weight[i]] + value[i])

  3. 遍历顺序
    必须遵从先遍历物品,再倒序遍历背包容量!

for(int i = 0; i < weight.size(); i++) { // 遍历物品
    for(int j = bagWeight; j >= weight[i]; j--) { // 遍历背包容量
        dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);

    }
}

按上述顺序(表格内为遍历顺序):

容量/物品012
036
125
214(左上角)

若为正序(表格内为遍历顺序):

容量/物品012
014
125(可能会考虑两次物品1)
23

二刷补充

关于遍历顺序:只看 递推公式 如何推出dp得每一个元素

  1. 如dp从 二维 到 一维,则在保证不覆盖原来值的情况下定遍历顺序
    例:本题从左上角推出dp[i][j],于是选择对背包容量倒序遍历,则能保证新的dp[j]能与原来的dp[j]进行比较;
    必须先遍历物品,再遍历背包,因为如相反,会导致dp[j - weight[i]] 此时没有更新仍为0
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值