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]); }