一.01背包
1.不一定装满:最普通的模型,随便搞= =,此时\(dp\)表示\(x\)体积能获得的最大价值。
2.恰好装x体积的最大价值:先把\(dp\)初始化一个极大负值,然后同上,若为负数则是不能恰好装到的。
3.能否恰好装满问题:同上。
二.完全背包
正着枚举就行了= =
三.多重背包:
体积为\(m\)的背包,有\(n\)种物品,第\(i\)种物品有\(ai\)个。
具体问题同01背包。
优化:
①二进制分组:把物品分成log个新物品,价值和体积分别是原物品的1,2,4,8……倍。然后用01背包去做。
int k=log(cnt[i])/log(2);
void js(int k,int j,int num)
{
int temp=1;
cc[++sum]=c[j];ww[sum]=w[j];
num-=1;
for(int i=1;i<=k-1;i++)
{
temp=temp*2;
cc[++sum]=c[j]*temp;ww[sum]=w[j]*temp;
num-=temp;
}
if(num>0)
{
cc[++sum]=c[j]*num;ww[sum]=w[j]*num;
}
}
话说以前的代码写的真丑
②单调队列优化 不会 背代码吧= =
for(int i=1;i<=n;i++)
{
scanf("%d%d%d",&c,&w,&s);
if(s>m/c) s=m/c;
for(int d=0;d<c;d++)
{
head=tail=1;
for(int k=0;k<=(m-d)/c;k++)
{
int tmp=dp[k*c+d]-w*k;
while(head<tail&&q[tail-1]<=tmp) tail--;
q[tail]=tmp; num[tail++]=k;
while(head<tail&&k-num[head]>s) head++;
dp[k*c+d]=max(dp[k*c+d],q[head]+w*k);
}
}
}
四.分组背包(泛化物品)
考虑这样的物品,它没有固定的体积和价值,它的价值随着分配给它的体积的变化而变化。抽象成数学模型是一个定义域为[0,V]中整数的函数H[V],对于每个在定义域中的v,对应一个价值H[v]。
求泛化物品的和:
for(int i=1;i<=t;i++)//遍历第i组
{
for(int j=m;j>=1;j--)
{
for(int k=1;k<=z[i];k++)//第i组的第k个元素
{
if(j-c[i][k]>=0) dp[j]=max(dp[j],dp[j-c[i][k]]+w[i][k]);
}
}
}
五.有依赖的背包
可以将依赖关系连边,看做森林。儿子更新父亲的时候看作两个泛化物品的合并。
其实就是树形\(dp\) \(emmm\),树上分组背包,详见有线电视网和hdu1561,一般需要数据较小。
还有一种特殊情况,依赖物品不是被依赖物品,这时候对附件集合进行01背包求出0~v-c[i]费用的最大价值,详见hdu3449。