1. 程式人生 > >背包問題的優化(洛谷1776 寶物篩選_NOI導刊)

背包問題的優化(洛谷1776 寶物篩選_NOI導刊)

提高 -1 數據 這樣的 math 重新 總數 max 多少

背包型dp,但是沒有看清數據範圍差點認為是水題了,(然後詭異的拿了20分)
標解是:2進制優化,比較簡單把每一類物品看做若幹個相互獨立的物品,放在一個另外的數組裏,然後全局跑一邊01就可以。
主要思想是:將一種物品分成1、2、3..一份,然後跑01(01的復雜度低啊!)
如果難以理解的話不妨舉個例子:如2=1+1;5=1+2+2;4=1+2+1;
就是說依次遞增,當最後一次的次數太大了使前面所有的和加起來大於次數了那麽我們就不繼續遞增拉
好理解的程序的話就是這樣的但是跑的慢(最後一個點500多ms!)

uses math;
var n,v,i,j,tc,tw,tt,cnt,pp:longint;
    w,c:array[
0..1000000]of longint; f:array[0..1000000]of longint; begin readln(n,v); cnt:=1; for i:=1 to n do begin readln(tc,tw,tt);//讀入一種物品的價值、代價、次數 pp:=1;//分解的第一種是1個分解 while true do begin//死循環拉~~ c[cnt]:=pp*tc;//加1個物品的物品 w[cnt]:=pp*tw; inc(cnt); dec(tt,pp);//減去次數 inc(pp);//下一種次數 if tt-pp<0
then break;//不夠了。。。 end; if tt>0 then begin//如果還有剩余次數那麽重新來一個把! c[cnt]:=tt*tc; w[cnt]:=tt*tw; inc(cnt); end; end; for i:=1 to cnt do//跑一組01就完事了; for j:=v downto w[i] do f[j]:=max(f[j],f[j-w[i]]+c[i]); writeln(f[v]); end.//收工!


(雖然上面是AC程序但是時間效率太低最後一點500ms,其實上面我們是分解123,但是現在可以按照二進制來做124)

其實這道題的本質是二進制的算法,我們考慮把這個物品換成若幹件物品,使得原問題中不論這種物品取多少件(0到最大件數p之間),都能等價於取若幹件代換以後的物品。且超過x件的策略必定不能出現。就是說,將每個物品分成若幹件01背包中的物品,其中每件物品有一個系數。這件物品的費用和價值均是原來的費用和價值乘以這個系數。(假設有一種價值為v,重量為w,限購次數為x的物品)令這些系數分別為1,2,2^2,…,2^(k-1),x+1-2^k,且k是滿足x+1-2^k>0的最大整數。由於加了位運算,解題的能力快速提高,時間比較快(雖然上面是可以AC但是下面更加優秀)

uses math;
var n,v,i,j,aa,cc,bb,cnt:longint;
    w,c:array[0..1000000]of longint;
    f:array[0..1000000]of longint;
begin
 readln(n,V);//讀入物品和背包體積
 cnt:=1;//分裂後待選的物品總數
 for i:=1 to n do begin
  readln(aa,bb,cc);//每一件物品的價值、體積、次數
  j:=1;
  while j<=cc do begin//分成若幹個物品
   w[cnt]:=j*bb;//第cnt件物品的體積=體積*次數
   c[cnt]:=j*aa;//第cnt件物品的價值=價值*次數
   cc:=cc-j;//次數分解
   j:=j<<1;//j乘以2
   inc(cnt);
  end;
  if cc>0 then begin//如果次數還有多余那麽一次性加
   w[cnt]:=cc*bb;
   c[cnt]:=cc*aa;
   inc(cnt);
  end;
 end;
 for i:=1 to cnt do
  for j:=v downto w[i] do
   f[j]:=max(f[j],f[j-w[i]]+c[i]);
 writeln(f[V]);
end.

背包問題的優化(洛谷1776 寶物篩選_NOI導刊)