背包DP
背包DP
背包DP,正如其名,一般有它的体积(容量m),物品的价值、个数、体积。但大多数的背包问题都比较抽象或者很复杂,因而找出题中的物品及其属性、背包的容积往往是破题的关键。
1. 01背包
该类型状态比较简单,一种物品只有拿或不拿两种状态,令f[i][j]表示在前i个物品中拿,剩余容量为j所能得到的最大价值,状态转移方程如下:
f[i][j]=max(f[i-1][j],f[i-1][j-v[i]]+w[i])
利用滚动数组可把空间复杂度降低一个维度
就变成了f[j]=max(f[j],f[j-v[i]]+w[i])
for(int i=1;i<=n;i++){
for(int j=m;j>=v[i];j--){
f[j]=max(f[j],f[j-v[i]]+w[i]);
}
}
2.多重背包
该类型题目有两种解题思路,一种是常规方法,即加一层循环物品个数。一种是进行二进制优化,进而转化为01背包,该方法效率也更高,因而应用比较广泛。
<法1:>
for(int i=1;i<=n;i++){
for(int j=m;j>=v[i];j--){
for(int k=1;k<=c[i];k++)
f[j]=max(f[j],f[j-k*c[i]]+k*w[i]);
}
}
for(int i=1;i<=n;i++){
for(int j=m;j>=v[i];j--){
for(int k=1;k<=c[i];k++)
f[j]=max(f[j],f[j-k*c[i]]+k*w[i]);
}
}
法2:>
for(int i=1;i<=n;i++){
for(int j=1;j<=c[i];j<<=1){
W[++t]=j*w[i];
V[t]=j*v[i];
}
if(c[i]>0){
W[++t]=j*w[i];
V[t]=j*v[i];
}
for(int i=1;i<=t;i++)
for(int j=m;j>=v[i];j--)
f[j]=max(f[j],f[j-v[i]]+w[i];
for(int i=1;i<=n;i++){
for(int j=1;j<=c[i];j<<=1){
W[++t]=j*w[i];
V[t]=j*v[i];
}
if(c[i]>0){
W[++t]=j*w[i];
V[t]=j*v[i];
}
for(int i=1;i<=t;i++)
for(int j=m;j>=v[i];j--)
f[j]=max(f[j],f[j-v[i]]+w[i];
3.完全背包
所拿物品没有个数限制,代码与01背包相似:
点击查看代码
for(int i=1;i<=n;i++)
for(int j=v[i];j<=m;j++)
f[j]=max(f[j],f[j-v[i]]+w[i];
4.分组背包
每组之中最多只能选一件物品。
//g[x][0]记录x组中物品个数,其余位置记录物品序号。
for(int i=1;i<=n;i++){
scanf("%d%d%d",&v[i],&w[i],&x);
g[x][++g[x][0]]=i;
}
for(int i=1;i<=t;i++){
for(int j=0;j<=m;j++){
f[i][j]=f[i-1][j];
for(int k=1;k<=g[i][0];k++){
if(j>=v[g[i][k]]){
x=g[i][k];
f[i][j]=max(f[i][j],f[i-1][j-v[x]]+w[x]);
}
}
}
}
5.混合背包
该题型其实就是把上面的背包类型(除了分组背包)揉在了一起,但其实还是按照多重背包来处理就行,对于没有数量限制的物品,就把它的数量上限定为包的容积/物品的体积,再二进制优化为01背包就可以很快解决了。
代码与多重背包高度相似,在这里我就不再展示出来了(doge)。
6.二维费用背包
该题型名字非常唬人,但实际做起来也就那么回事,就是体积不唯一,可能有许多因素,只要在循环体积的位置分开循环其余因素即可。
for(int i=1;i<=n;i++){
for(int j=mm;j>=m[i];j--)
for(int k=mv;k>=v[i];k--)
f[k][j]=max(f[k][j],f[k-v[i]][j-m[i]]+w[i]);
例:NASA的食物计划
题目描述:
航天飞机的体积有限,当然如果载过重的物品,燃料会浪费很多钱,每件食品都有各自的体积、质量以及所含卡路里,在告诉你体积和质量的最大值的情况下,请输出能达到的食品方案所含卡路里的最大值,当然每个食品只能使用一次.
输入格式:
第一行两个数体积最大值(<400)和质量最大值(<400)
第二行 一个数 食品总数N(<50).
第三行-第3+N行
每行三个数 体积(<400) 质量(<400) 所含卡路里(<500)
输出格式:
一个数所能达到的最大卡路里(int范围内)
本题食物的体积和质量都成为了背包的体积,食物的卡路里成为了价值,再进行01背包即可。
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int N=1005;
int mm,mv,t,n,i,j,k;
int f[N][N],w[N],v[N],m[N];
int main(){
cin>>mv>>mm>>n;
for(i=1;i<=n;i++)scanf("%d%d%d",&v[i],&m[i],&w[i]);
for(i=1;i<=n;i++){
for(j=mm;j>=m[i];j--)
for(k=mv;k>=v[i];k--){
f[k][j]=max(f[k][j],f[k-v[i]][j-m[i]]+w[i]);
}
}
cout<<f[mv][mm];
return 0;
}
// 注意不要把质量和体积一起循环,一定要分开循环!!!!
浙公网安备 33010602011771号