p1915//背包小结

一道经典的有依赖的背包问题.

我们先从01背包说起.有一群物品给定了重量w和收入v,问在一定资金下最大的收入是多少.作为一道经典的背包大家应该都会,o[i]表示i块钱时的最大收入,每个物品都可以使价格i从o[i-w]转移过来,即o[i]=max(o[i],o[i-w]+v);所以读入每个物品的时候可以循环一遍o用来更新.复杂度是nv;

然后说分组背包.有k组物品,每个组内的物品只能用一个,这个时候我们需要二维数组o[i][f]表示第i组花f块钱时的最大收入.对于每个物品还是可以使第i组价格f从o[i-1][f-w]转移过来,即o[i][f]=max(o[i][f],o[i-1][f-w]+v);

对于有依赖的背包问题,我们可以枚举每个物品选与不选的2^n-1种情况组成2^n-1个物品,这些物品两两间是不能同时取的.由于有多个游戏机,本题应该先用二进制模拟预处理出2^n-1个物品后用分组背包.

但是!这样写是会超时的.50*1024*100000=超时.会少很多很多分.

这个时候怎么办呢?继续分组应该是不行的,因为100000一定会出现在表达式中,一旦乘1024就会超时,更不要说那个50和调用函数的时间了(分组我写的是一个dfs...)那么改怎么做呢?

我们考虑为什么不能用01背包继续做这个题?因为游戏机本身需要钱.因为不能确定应该用w转移还是w+gp_j转移,假如这个点没有被本组内物品更新过是应该加上pg_j的,否则就不应该加.所以所以会出现这样的情况.

好像一筹莫展了?为什么不直接把上一组的答案加上gp_j后再来做01呢?不是等价的么?

for(i=v;i<=sum;i++)
    O[i]=o[i-v];
for(i=1;i<=n;i++)
{
    tv=read();tw=read();
    for(f=sum;f>=tv+v;f--)
        O[f]=max(O[f],O[f-tv]+tw);
}
for(i=0;i<=sum;i++)
    o[i]=max(o[i],O[i]);

本题结束,就这几行的内容最重要.

using namespace std;
int i,f;
int n,v,tv,tw,sum,T;
int O[100010],o[100010];
int main()
{
    T=read();sum=read();
    for(;T;T--)
    {
        v=read();n=read();
        for(i=v;i<=sum;i++)
            O[i]=o[i-v];
        for(i=1;i<=n;i++)
        {
            tv=read();tw=read();
            for(f=sum;f>=tv+v;f--)
                O[f]=max(O[f],O[f-tv]+tw);
        }
        for(i=0;i<=sum;i++)
            o[i]=max(o[i],O[i]);
    }
    for(i=1;i<=sum;i++)
        o[0]=max(o[0],o[i]);
    write(o[0]);
}

 

posted @ 2018-10-22 21:20  zzuqy  阅读(168)  评论(0编辑  收藏  举报