01背包问题(给定某种物品)
for(int i=n;i>=1;i--){ //dp[i][j]表示处于从第i个阶段到最后一个阶段的最大花费
for(int j=0;j<=m;j++){ //必须遍历所有的状态
if(j<=weight[i]) dp[i][j] = dp[i+1][j];
else dp[i][j] = max(dp[i+1][j],dp[i+1][j-weight[i]]+val[i]);
}
}
for(int i=1;i<=n;i++){ //表示取了第i件物品的最大价值
for(int j=weight[i];j<=m;j++){ //必须遍历所有的状态
if(j<=weight[i]) dp[i][j] = dp[i-1][j];
else dp[i][j] = max(dp[i][j],dp[i-1][j-weight[i]]+val[i]);
}
}
滚动数组:
- 如果题目求背包必须装满情况下的最大值,那么dp[0] = 0,其余为-inf。因为背包可以在什么都不装的情况下达到最大值0。而在其它容量下,还是未知的。如果最后dp[V] = -inf,则表明不存在这样的容量V被恰好装满。
- 如果题目求不用全部装满情况下的最大值,那么dp[0…n] = 0。因为存在合法情况就是什么都不转,其价值为0。
for(int i=1;i<=n;i--){ //滚动数组,不用转移状态
for(int j=m;j>=weight[i];j--)
dp[j] = max(d1[j],dp1[j-weight[i]]+val[i]); }
完全背包问题(物品无穷多)
for (int i = 1; i <= n; i++) {
for (int j = weight[i]; j <= m; j++) { //注意顺序,利用当前的状态
f[j] = max(f[j], f[j - weight[i]] + value[i]);
}
}
优化:
-
将价值小的而体积大的去掉。
-
将体积大于容量的去掉。
多重背包问题(限定物品的数量)
转换为01背包问题
for(int i=1;i<=n;i++){
int v,w,num;
scanf("%d%d%d",&v,&w,&num);
while(num--) val[++tot] = v, weight[tot] = w;
}
二进制优化
for(int i=1;i<=n;i++){
int v,w,num;
scanf("%d%d%d",&v,&w,&num);
int c = 1;
while(num > c){
val[++tot] = c*v, weight[tot] = c*w;
num -= c;
c *= 2;
}
val[++tot] = num*v, weight[tot] = num*w;
}
单调队列优化
stl版本
for(int i=1;i<=n;i++){
scanf("%d%d%d",&weight,&val,&num);
for(int a=0;a<weight;a++){ //枚举余数
qv.clear(),qn.clear(); //前者维护最大值,后组维护对应的数量
for(int k=0;a+k*weight<=v;k++){
int tmp = dp[a+k*weight] - k*val;
if(!qv.empty() && qn.front()+num+1==k) qv.pop_front(), qn.pop_front();
while(!qv.empty() && qv.back()<=tmp) qv.pop_back(),qn.pop_back();
qv.push_back(tmp);
qn.push_back(k);
dp[a+k*weight] = qv.front() + k*val;
}
}
}
数组模拟版本
for(int i=1;i<=m;i++){
scanf("%d%d%d",&weight,&val,&num);
for(int a=0;a<weight;a++){ //枚举余数
int head = 0,body = 0;
for(int k=0;a+k*weight<=v;k++){
int tmp = dp[a+k*weight] - k*val;
if(qn[head] + num + 1 == k) head++;
while(head < body && qv[body-1] <= tmp) body--;
qn[body] = k;
qv[body++] = tmp;
dp[k*weight+a] = qv[head] + k*val;
}
}
}
二维费用背包问题
for(int i=1;i<=n;i++){
scanf("%d%d%d%d",&v,&u,&val); //v表示费用1,u表示费用2
for(int j=s1;j>=v;j--){ //s1,s2分别表示限制容量
for(int k=s2;k>=u;k--){
dp[j][k] = max(dp[j][k],dp[j-v][k-s2]+val);
}
}
}
int ans = 0; //求最大价值,注意最后结果不是dp[s1][s2] ??
for(int i=0;i<=s1;i++){
for(int j=0;j<=s2;j++){
ans = max(ans,dp[i][j]);
}
}
比如背包容量为12
物品1 价值10,体积12 所以dp[12][1] = 10
物品2 价值20,体积8, 所以dp[8][1] = 20