自学背包问题的整理笔记

一.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。

猜你喜欢

转载自www.cnblogs.com/sqrthyy/p/9746505.html