多次背包
多次背包问题:给定 n 种物品和一个背包。第 i 种物品 的价值是 Wi ,其体积
为 Vi,数量是 Ki件,背包的容量为 C。可以任意选择装入背包中的物品,求装入背
包中物品的最大总价值。

方法一:可以把此物品拆分成Ki个只能用一次的物品,直接套用 0-1 背包问题的经典动规实现,但是效率太低了,需要寻找更高效的算法。此算法时间复杂度为O(C*∑(Ki))

方法二:拆分成体积和价值分别为原来1, 2 , 4..   2^m,    Ki-2^m 倍的几个物品,用0-1背包求解。 时间复杂度为O(C*∑([log2Ki]))

方法三(本文重点):(对单调队列没有了解的请参见原论文[本文结尾链接])对于第 i 种物品来说,已知体积 v,价值 w,数量 k,那么可以按照当前枚举的体积 j 对v的余数把整个动规数组分成 v份,以下是 v=3 的情况:
j             0 1 2 3 4 5 6 7 8 ……
j mod v   0 1 2 0 1 2 0 1 2 ……

我们可以把每一份分开处理,假设余数为 d。
编号j         0     1        2            3           4             5         ……
对应体积 d    d+v    d+2*v     d+3*v    d+4*v      d+5*v    ……

现在看到分组以后,编号 j 可以从 j-k 到 j-1 中的任意一个编号转移而来(因为相邻的体积正好相差 v) ,这看上去已经和区间最大值有点相似了。但是注意到由于体积不一样,显然体积大的价值也会大于等于体积小的,直接比较是没有意义的,所以还需要把价值修正到同一体积的基础上。比如都退化到 d,也就是说用 F[j*v+d]- j*w来代替原来的价值进入队列。

对于物品i,伪代码如下

1. FOR d: = 0 TO v-1                     //枚举余数,分开处理
2.   清空队列
3.   FOR j: = 0 TO (C-d) div v           //j 枚举标号,对应体积为 j*v+d
4.    INSERT j , F[ j*v+d ] – j * w      //插入队列
5.    IF A[ L ] < j - k THEN L + 1 → L //如果队列的首元素已经失效
6.    B[ L ] + j * w → F[ j*v+d ]       //取队列头更新
7.   END FOR
8. END FOR

已知单调队列的效率是 O(n),那么加上单调队列优化以后的多次背包,
效率就是 O(n*C)了。
(详细请参见原论文)

==========================================================

完整程序如下(Pascal):

var
   a,b,f:array[0..100000] of longint;
   m,s,c,n,t,i,j,l,r,d:longint;
procedure insert(x,y:longint);
begin
   while (l<=r)and(b[r]<=y) do dec(r);
   inc(r);a[r]:=x;b[r]:=y;
end;
begin
   readln(n,t);              //读入数据 n为物品个数 t为背包容量
   for i:=1 to n do
   begin
      read(m,s,c);         //读入当前物品 m为物品体积、s为物品价值、c为物品可用次数(0表示无限制)
      if (c=0)or(t div m<c) then c:=t div m;
      for d:=0 to m-1 do
      begin
         l:=1;r:=0;     //清空队列
         for j:=0 to (t-d) div m do
         begin
            insert(j,f[j*m+d]-j*s);   //将新的点插入队列
            if a[l]<j-c then inc(l);   //删除失效点
            f[j*m+d]:=b[l]+j*s;        //用队列头的值更新f[j*m+d]
         end;
      end;
   end;
   writeln(f[t]);
end.

==========================================================

本文内容参考国家集训队2009论文 徐持衡《浅谈几类背包题》

原文地址:http://wenku.baidu.com/view/8ab3daef5ef7ba0d4a733b25.html

==========================================================

其实这只是一种算法,另一种O(VN)算法见:http://hi.baidu.com/sy2006ppkdc/blog/item/f86374256d84a91a8b82a131.html(此方法使用类似线性动规的算法,常数稍大,内存较大)