01背包问题
有 n 件物品,每件物品重量为 w[i] ,价值为 value[i] 。现有一个容量为V的背包,问如何选取物品放入背包,使得背包物品的总价值最大。
每件物品仅有1件。
样例:
n=5,V=8;
3 5 1 2 2 //w[i]
4 5 2 1 3 //value[i]
解题详情
-
选择策略
开辟一个长度为V+1
的数组 d p dp dp,下标i
代表背包容量,数组存储的数值是所能装的🔍最大价值。
前i
件物品恰好放进容量为a
的背包中,可以获得的最大价值。有两种情况,
1️⃣一种是不放进去的历史i-1
状态;
2️⃣一种是把这个物品放进去之后,即价值增加后的状态。
保留总价值更大的那个状态
由此获得状态转移方程- 状态转移方程
d p [ a ] = m a x { d p [ a ] , d p [ a − w [ i ] ] + v a l u e [ i ] } 其中 ( 1 ≤ i ≤ n , w [ i ] ≤ a ≤ V ) dp[a]=max\{ dp[a] , dp[a-w[i]]+value[i] \} \\其中(1\leq i \leq n , w[i] \leq a \leq V) dp[a]=max{dp[a],dp[a−w[i]]+value[i]}其中(1≤i≤n,w[i]≤a≤V)
⭕️从限制条件看,i
控制物品放入,a
控制背包容量的大小。
max()
的第一个值dp[a]
代表第1️⃣种不放入当前第i
个物体的情况。
⟺ \iff ⟺之前的历史状态。
第二个值dp[a-w[i]]+value[i]
代表第2️⃣种放入当前第i
个物体的情况
⟺ \iff ⟺退化到背包容量为a-w[i]
的状态,然后该背包的价值基础上,加上该物品的价值value[i]
👻如果只开辟了一维数组 d p dp dp空间,每一次进行选择时,更新 d p dp dp一定要【倒序】。
- 状态转移方程
-
🌰利用样例进行解释。
第0️⃣轮:
dp[]
数组原始全为0。
第1个物品的重量为w[0]=3
,其价值为value[0]=4
,
那么背包容量a>=3
的背包都可以放下这个物品,将a>=3
的背包总价值记录下来【数组的value】
第1️⃣轮:dp
数组上一轮的结束状态是{0,0,0,4,4,4,4,4,4}
。
第2个物品的重量为w[1]=5
,其价值为value[1]=5
,
背包容量a>=5
都放得下该物品。
此时就要进行选择策略,需要使用到状态转移方程max(dp[a], dp[a-w[i]]+value[i])
。
【倒序更新】a=8
的背包,👎 【不放入】历史状态为dp[8]=4
,👍 【放入】背包退化成dp[a-w[0]]=dp[8-5]=dp[3]
的状态(也就是背包容量减去当前物体的重量,以保证我放入当前物品时,整个背包的最终重量weight=8),此时总价值为dp[8]=dp[3]+value[1]=4+5=9
✌️max(4, 9)
显然选择【放入】,把当前物体装进去可以使a=8
的背包总价值更大。
a=7
的背包,👎 【不放入】历史状态为dp[7]=4
,👍【放入】背包退化dp[7-5]=dp[2]=0
,请注意这里的dp[2]
时上一轮结束后的背包状态值。放入当前物品后,总价值为dp[7]=dp[2]+value[1]=0+5=5
。
✌️max(4, 5)
选择【放入】。也就是说,当前a=7
的背包总价值最大,只有把之前放入的物品扔掉,换成当前物品,价值更大。
剩下的以此类推即可~
样例代码
通过下列代码,你可以只管的看到每一轮最大价值数组 d p dp dp的状态是什么样的。以此验证背包问题的解决。
#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
int main() {
int n=5, V=8;
int w[5], c[5], dp[9];
for (int i = 0; i < n; i++) {
cin >> w[i];
}
for (int i = 0; i < n; i++) {
cin >> c[i];
}
for (int v = 0; v <= V; v++) {//初始化dp数组
dp[v] = 0;
}
for (int i = 0; i < n; i++) {
cout << "当前w[i] = " << w[i] << endl;
for (int v = V; v >= w[i]; v--) {
dp[v] = max(dp[v], dp[v - w[i]] + c[i]);
cout << "重量为 v= " << v << "的物体 -->dp[v] =" << dp[v] << endl;
}
}
int max = 0;
for (int v = 0; v <= V; v++) {
if (dp[v] > max) {
max = dp[v];
}
}
cout << "max = " << max << endl;
return 0;
}
所有参考自胡凡的《算法笔记》。动画纯自制,本篇仅作为自己学习笔记,并无抄袭之意。有任何问题可以评论聊~