01背包问题2-一维滚动数组-python

本笔记是相对于之前的笔记:01背包问题1使用的二维数组,dp[i][j]。这里的dp[i][j]含义为,当[0,i]个物品加入到最多能包含重量j的背包中时,背包所能有的最大价值。dp[i][j] = max(dp[i-1][j],dp[i-1][j-weight[i]]+value[i])。这里,dp[i]的值是由dp[i-1]推导而来的,如果我们将dp[i-1]的值完全赋给dp[i],那么,dp[i][j] = max(dp[i][j],dp[i][j-weight[i]]+value[i])。

那么,我们是不是可以直接用一维数组dp[j]来表达呢,通过先遍历物品,再遍历背包,每一次遍历都看一下是否能装入该物品,并且根据上一层还未装入i物品时的dp[j-weight[i]]+value[i]来和自身的dp[j]做对比,得到是否装入该物品的结论。

1.dp
dp[j]:表示当背包最大承受重量为j时,能获得的最大价值。
2. 状态转移
dp[j] = max(dp[j-weight[i]]+value[i],dp[j])
3. 初始化
dp[0]=0
4. 遍历顺序
这个时候,针对于背包的遍历我们需要注意,在01背包问题1中,我们可以看到背包的遍历是从小到大的。这是因为,他本身就依赖上一层,也就是未装入物品i时候的dp[i-1]来进行计算,但在这里就显然行不通了。为什么呢?别着急,我来举个例子。

因为是一维数组,dp[j]的计算本来就依赖前面更新之后的dp[j-weight[i]],那么如果这个时候,从小到大遍历背包以至于dp[j-weight[i]]中也加入了i会造成什么后果呢?
比如,dp初始值都为0,在遍历物品0时,物品0重量为1,背包w=1,那么这个时候,因为1>=weight[0], 我们就要考虑是否加入物品0。那么dp[1] = max(dp[1],dp[1]+value[0])=max(0,0+value[0])=value[0]。好,那这个时候,我们就在dp[1]中加入了物品0。
那么当背包w=2时,2也>weight[0],我们也要考虑是否加入。dp[2] = max(dp[2],dp[2-weight[0]]+value[0])=max(0,dp[1]+value[0])=max(0,value[0]+value[0]). 这个时候我们就会发现,我们在背包w=2时,加入了两遍value[0]。

这是为什么呢?这就是因为我们之前讲的,我们所依赖的计算,应该是未加入该物品的前一个背包值。而当我们从小到大遍历背包时,小背包也会加入这个物品,那么我们再加入这个物品的话,就变成了加入了两次这个物品到我们的背包里,显然是不合理的。所以,最好的解决办法就是将背包从大到小遍历,这样的话,每次判断是否加入一个新的物品,大背包的dp值先更新,而大的值所借鉴的就是上一层,未加入该新物品时小背包的dp值。

for i in range(n):
	for j in range(w,0,-1):
				if j < weight[i]:
					dp[j] = dp[j]
				else:
					dp[j]  = max(dp[j-weight[i]],dp[j])

5.举例
w = 4
weight = [1,3,4]
value = [15,20,30]
dp[0] = 0
dp[4] = 15
dp[3] = 15
dp[2] = 15
dp[1] = 15
dp[4] = max(15+20,15)=35
dp[3] = max(0+20,15) = 20
dp[2] = 15
dp[1] = 15
dp[4] = max(0+30,35)=35
dp[3]=20
dp[2] = 15
dp[1] = 15
return 35
完整代码如下:

w = 4
weight = [1,3,4]
value = [15,20,30]
def package02(w, weight, value):
	n = len(weight)
	dp = [0]*(w+1)
	dp[0] = 0
	for i in range(n):
		for j in range(w,0,-1):
			if j < weight[i]:
				dp[j] = dp[j]
			else:
				dp[j] = dp[j-weight[i]]+value[i]
	return dp
print(package02(w, weight, value))

本文参考:卡尔背包

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值