多次背包
多次背包問題:給定 n 種物品和一個背包。第 i 種物品 的價值是 Wi ,其體積
為 Vi,數量是 Ki件,背包的容量為 C??梢匀我膺x擇裝入背包中的物品,求裝入背
包中物品的最大總價值。

方法一:可以把此物品拆分成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(此方法使用類似線性動規的算法,常數稍大,內存較大)