动态规划——01背包问题

文章介绍了动态规划解决01背包问题的两种方法,一是使用二维数组实现,二是利用滚动数组优化成一维数组实现。在二维数组方法中,通过初始化和两层循环更新dp数组来找到最大价值。而在一维数组实现中,通过只保留上一行的dp数组和当前行,从后往前遍历更新dp,达到空间优化的目的。
摘要由CSDN通过智能技术生成

理论1-用二维数组实现

背包问题,有一种写法, 是使用二维数组,即dp[i][j] 表示从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少
在这里插入图片描述

确定递推公式
有两个方向推出来dp[i][j],
若当前背包容量j<weight[i] (只放一个物品i都不够),此时只能从0-(i-1)物品中选取,所以等于dp[i-1][j];

若当前背包容量j>=weight[i]:可以选择放入物品[i],也可以不放
放入物品[i]:当前价值dp[i][j]=val[i] + dp[i-1][ j-weight[i] ]
注:放如i的总价值等于i的价值加上剩余背包容量 j-weight[i] 下放入0-(i-1)物品获得的最大价值
不放入物品[i]:当前价值dp[i][j] = dp[i-1][j]
即:等于相同背包容量下只放入0-(i-1)物品的最大价值
这种情况下两者取最大得到dp[i][j]

遍历顺序
由递推公式知,dp[i][j]由其左上角和上方元素计算得出,所以可以先按层遍历,也可以先按列遍历。

初始化
dp[i][j]都和 dp[i-1][j] 相关,所以需要dp[0][j],若物品0的重量小于等于包容量,则dp[0][j]相应位置为物品0的价值;若物品0的重量超过包容量,则物品0不能入包,价值为0

dp[i][j]还与 dp[i-1][ j-weight[i] ] 相关,所以初始化dp[i][0],易知当物品重量均大于0,包容量为0的情况下,dp[i][0]=0

其余位置在之后的遍历过程中都会被覆盖,为初始化的简单,可将整个数组先初始化为全0,再对dp[0][j]做修改

理论2-用一维数组(滚动数组)实现

根据上面的递推公式,dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);dp[i][j]仅与上一层的dp有关,所以不用维护整个数组,可只保存上一层dp和当前层dp,进一步,可压缩至只使用一个数组dp[j]:容量为j的背包,所背的物品价值最大为dp[j]

递推公式:
dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
这里右端的dp[j] 相当于 二维dp数组中的dp[i-1][j],即不放物品i

初始化:
当j<weight[i],放不进物品i,当前能背的最大价值和不放物品i相同,所以当前位置不需要修改,只用讨论j>=weight[i]的情况
设想第一行,物品0,不管怎样只能放入一个物品,dp[j]要么是0要么是val[0],而递推公式中可以取最大,所以初始化为全0即可(dp[0]一定是0)

遍历顺序:
由于需要用到之前的dp,要从后向前遍历,防止前面的dp被覆盖而不能查询到上一层的dp
注:外层是遍历物品,内层遍历背包容量,顺序不可变
理由:若外层遍历背包,内存遍历物品,因为是倒序便利的,所以背包里只放入了一个物品(第0个),不满足要求

小试牛刀

背包最大重量为4。

物品为:

重量 价值
物品0 1 15
物品1 3 20
物品2 4 30
问背包能背的物品最大价值是多少?

# 二维数组
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)
# 一维数组
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)):
        for j in range(bag_weight, weight[i] - 1, -1):  # 倒序 只用修改大于等于weight[i]的j
            # 递归公式
            dp[j] = max(dp[j], dp[j - weight[i]] + value[i])

    print(dp)

test_1_wei_bag_problem()

面试要求

写出二维背包代码
两个for循环嵌套顺序为什么这么写,能不能调换
初始化逻辑

写出一维背包代码
两个for循环嵌套顺序能不能调换

来源:
01背包问题
01背包问题一维数组实现

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值