代码随想录 —完全背包
完全背包指的是每个物品可以 使用无数次。
求得也是给一个背包或者容器装满背包后的最大价值是多少。
区别在遍历顺序上的问题,一维01背包的数组在背包上的遍历只能是倒序,但是在完全背包这里是可以是正序遍历的。
为什么 01背包是倒序的,但是完全背包是正序的?
让我们先看一下两段的代码 :
01背包
for(int i=0;i<n;i++) { //n为商品的个数
for(int j=m;j>=0;j--){ //m为背包的最大容量
if(j>=weight[i])
dp[j] = max(dp[j],dp[j-weight[i]]+value[i]);
}
}
完全背包
for(int i=0;i<n;i++) { //n为商品的个数
for(int j=0;j<=m;j++){ //m为背包的最大容量
if(j>=weight[i])
dp[j] = max(dp[j],dp[j-weight[i]]+value[i]);
}
}
接下来我们通过模拟循环来尝试理解这个问题:为什么01背包是需要是需要是在遍历背包的容量的时候使用的是倒序循环而完全背包是正序的?
- 在第一个物品1的时候,我们会从后到前来进行装入到背包中,只要背包的容量大于物品1的重量。(其实在针对物品1的时候倒序或者正序是没什么区别的,因为都相当于把物品1装入到所有重量大于物品1的背包中)。
其实这里是有问题的,因为其实在完全背包的时候,当 j = 2 ∗ w e i g h t [ 0 ] j =2*weight[0] j=2∗weight[0]的时候,完全背包就已经开始
d p [ w e i g h t [ 0 ] ∗ 2 ] = v a l u e [ 0 ] ∗ 2 dp[weight[0]*2] = value[0]*2 dp[weight[0]∗2]=value[0]∗2了。理由就是如下的。
- 当进入到物品2的时候,我们先假设从后到前进行遍历背包的容量来遍历背包:
假设 ( w e i g h t [ 0 ] > w e i g h t [ 1 ] a n d v a l u e [ 0 ] < v a l u e [ 1 ] ) (weight[0] > weight[1] \ \ and \ value[0]< value[1]) (weight[0]>weight[1] and value[0]<value[1])
通过** d p [ j ] = m a x ( d p [ j ] , d p [ j − w e i g h t [ i ] ] + v a l u e [ i ] ) dp[j] = max(dp[j],dp[j-weight[i]]+value[i]) dp[j]=max(dp[j],dp[j−weight[i]]+value[i])** 来进行调整dp的值,当我们进入** d p [ w e i g h t [ 0 ] ∗ 2 ] dp[weight[0]*2] dp[weight[0]∗2]**的时候
d p [ w e i g h t [ 0 ] ∗ 2 ] = m a x ( d p [ w e i g h t [ 0 ] ∗ 2 ] , d p [ w e i g h t [ 0 ] ∗ 2 − w e i g h t [ 1 ] ] + v a l u e [ 1 ] ) dp[weight[0]*2] = max(dp[weight[0]*2],dp[weight[0]*2-weight[1]]+value[1]) dp[weight[0]∗2]=max(dp[weight[0]∗2],dp[weight[0]∗2−weight[1]]+value[1])
通过假设条件,我们可以得出来 d p [ w e i g h t [ 0 ] ∗ 2 ] = v a l u e [ 1 ] + v a l u e [ 0 ] dp[weight[0]*2] = value[1]+value[0] dp[weight[0]∗2]=value[1]+value[0]
理由如下:
在第一层循环即物品1的时候
d
p
[
w
e
i
g
h
t
[
0
]
∗
2
−
w
e
i
g
h
t
[
1
]
]
dp[weight[0]*2-weight[1]]
dp[weight[0]∗2−weight[1]]已经等于
v
a
l
u
e
[
0
]
value[0]
value[0]了,因为
w
e
i
g
h
t
[
0
]
∗
2
−
w
e
i
g
h
t
[
1
]
=
w
e
i
g
h
t
[
0
]
+
w
e
i
g
h
t
[
0
]
−
w
e
i
g
h
t
[
1
]
weight[0]*2-weight[1] = weight[0]+weight[0]-weight[1]
weight[0]∗2−weight[1]=weight[0]+weight[0]−weight[1] 又因为
w
e
i
g
h
t
[
0
]
>
w
e
i
g
h
t
[
1
]
weight[0]>weight[1]
weight[0]>weight[1]所以
w
e
i
g
h
t
[
0
]
∗
2
−
w
e
i
g
h
t
[
1
]
>
w
e
i
g
h
t
[
0
]
weight[0]*2-weight[1]>weight[0]
weight[0]∗2−weight[1]>weight[0]。
- 如果我们使用的是从前到后来进行装入物品2 。依旧是通过 d p [ j ] = m a x ( d p [ j ] , d p [ j − w e i g h t [ i ] ] + v a l u e [ i ] ) dp[j] = max(dp[j],dp[j-weight[i]]+value[i]) dp[j]=max(dp[j],dp[j−weight[i]]+value[i])这个公式来进行判断。当我们计算 d p [ w e i g h t [ 0 ] ∗ 2 ] dp[weight[0]*2] dp[weight[0]∗2]的时候。
d p [ w e i g h t [ 0 ] ∗ 2 ] = m a x ( d p [ w e i g h t [ 0 ] ∗ 2 ] , d p [ w e i g h t [ 0 ] ∗ 2 − w e i g h t [ 1 ] ] + v a l u e [ 1 ] ) dp[weight[0]*2] = max(dp[weight[0]*2],dp[weight[0]*2-weight[1]]+value[1]) dp[weight[0]∗2]=max(dp[weight[0]∗2],dp[weight[0]∗2−weight[1]]+value[1])
这个表达式的结果应该是 d p [ w e i g h t [ 0 ] ∗ 2 ] = v a l u e [ 1 ] ∗ 2 dp[weight[0]*2] = value[1]*2 dp[weight[0]∗2]=value[1]∗2 ,为什么呢?因为在第二层循环的从前向后遍历我们可以得到
d p [ w e i g h t [ 0 ] ∗ 2 − w e i g h t [ 1 ] ] = v a l u e [ 1 ] dp[weight[0]*2-weight[1]] = value[1] dp[weight[0]∗2−weight[1]]=value[1],已经把 d p [ w e i g h t [ 0 ] ∗ 2 − w e i g h t [ 1 ] ] dp[weight[0]*2-weight[1]] dp[weight[0]∗2−weight[1]]更新过了,所以当再进行使用 m a x max max式子进行判断的时候此时已经发生改变了,所以会造成重复装入,此时就是完全背包问题。