1.0/1揹包
var v,p:array[0..1000] of longint;
f:array[0..100000] of longint;
n,m,i,j:longint;
function max(a,b:longint):longint;
begin
if a>b then exit(a); exit(b);
end;
begin
readln(n,m);
for i:=1 to n do readln(v[i],p[i]);
for i:=1 to n do
for j:=m downto v[i] do
f[j]:=max(f[j],f[j-v[i]]+p[i]);
writeln(f[m]);
end.
2.完全揹包
var v,p:array[0..1000] of longint;
f:array[0..100000] of longint;
n,m,i,j:longint;
function max(a,b:longint):longint;
begin
if a>b then exit(a); exit(b);
end;
begin
readln(n,m);
for i:=1 to n do readln(v[i],p[i]);
for i:=1 to n do
for j:=v[i] to m do
f[j]:=max(f[j],f[j-v[i]]+p[i]);
writeln(f[m]);
end.
note:0/1揹包和完全揹包中有時要求揹包必須裝滿,其實等價於只能從f[0]轉移過來.
3.多重揹包
先貼上樸素**:
var v,p,c:array[0..1000] of longint;
f:array[0..100000] of longint;
n,m,i,j,k:longint;
function max(a,b:longint):longint;
begin
if a>b then exit(a); exit(b);
end;
begin
readln(n,m);
for i:=1 to n do readln(v[i],p[i],c[i]);
for i:=1 to n do
for k:=1 to c[i] do
for j:=m downto v[i] do
f[j]:=max(f[j],f[j-v[i]]+p[i]);
writeln(f[m]);
end.
複雜度o(nmc),聯賽不太可能考這麼裸的.
以下是幾種優化的演算法.
1.二進位制拆包:
var v,p:array[0..1000,0..20] of longint;
f:array[0..100000] of longint;
c:array[0..1000] of longint;
n,m,i,j,k,l,vv,pp,cc:longint;
function max(a,b:longint):longint;
begin
if a>b then exit(a); exit(b);
end;
begin
readln(n,m);
for i:=1 to n do
begin
readln(vv,pp,cc);
l:=trunc(ln(cc)/ln(2));
for j:=0 to l-1 do
begin
v[i,j]:=vv*(1<0 then
begin
inc(c[i]);
v[i,l]:=vv*(cc-1<
note:
設乙個物品的個數為c.
設k是滿足c-2^(k+1)+1>0的最大整數.
則將物品拆分為係數分別為1,2,4,...,2^k,c-2^(k+1)+1的物品,當然,要乘上體積、價值.
為什麼可以這樣拆?
因為上面的係數可以組成0~c的所有數.簡單證明如下:
首先,1,2,4,...,2^k可以組合出0~2^(k+1)-1的所有整數.
而c-2^(k+1)+1<=2^(k+1)-1,否則將不滿足"k是滿足c-2^(k+1)+1>0的最大整數"
所以2^(k+1)~c的整數也全部可以被表示.
複雜度o(nm∑logci),可以對付一般的題目.
2.單調佇列優化
type rec=record
v,p:longint;
end;
var f:array[0..10001] of longint;
q:array[0..100000] of rec;
n,m,i,j,h,t,v,p,c,mc,r:longint;
function min(a,b:longint):longint;
begin
if a>b then exit(b); exit(a);
end;
procedure insert(pos,val:longint);
begin
while (h<=t)and(q[t].v<=val) do dec(t);
inc(t);
q[t].p:=pos;
q[t].v:=val;
end;
begin
readln(n,m);
for i:=1 to n do
begin
readln(v,p,c);
mc:=min(c,m div v);
for r:=0 to v-1 do
begin
h:=1; t:=0;
for j:=0 to (m-r) div v do
begin
insert(j,f[j*v+r]-j*p);
if j-q[h].p>mc then inc(h);
f[j*v+r]:=q[h].v+j*p;
end;
end;
end;
writeln(f[m]);
end.
note:
我們先來觀察普通的方程(對於乙個物品):
f[j]=max (1<=k<=c,v<=j<=m)
複雜度為o(nmc).
怎麼優化?
考慮決策點j,將其按模v的餘數分類(分類轉移並不影響決策的正確性).
對於每一類j,設p=[j/v],r=j-p*v (即j mod v),則j=p*v+r
那麼j可以從l=q*v+r轉移過來,其中q>=0 & p-c<=q<=p.
隨著j的增加,p也增加,q的下界也在增加(單調性!).
將方程改寫一下:
f[p*v+r]=max (q>=0 & p-c<=q<=p)
即f[p*v+r]=max+p*v
(這裡r是人工列舉的,視為常量)
演算法已經出來了:
for 每乙個物品 do
for r=0 to v-1 do
}複雜度為o(nm),應該不會被卡掉..
多重揹包的優化告一段落.
再寫一種叫做陣列標記的演算法(建議先看poj1742).
對於poj1742這樣的題目,陣列標記具有**短,易編寫,易除錯,效率高等等優點.
核心思想是用乙個count陣列記錄體積為j時,當前物品使用了多少個.具體見**:
var f:array[0..100001] of boolean;
count:array[0..100001] of longint;
a,c:array[0..101] of longint;
n,m,i,j,ans:longint;
begin
while not seekeof do
begin
readln(n,m);
if n+m=0 then break;
fillchar(f,sizeof(f),0);
f[0]:=true;
ans:=0;
for i:=1 to n do read(a[i]);
for i:=1 to n do read(c[i]);
readln;
for i:=1 to n do
begin
fillchar(count,sizeof(count),0);
for j:=a[i] to m do
if(not f[j])and(f[j-a[i]])and(count[j-a[i]]
複雜度o(nm)
這種演算法相當優秀,但是有使用的侷限性(不然要單調佇列幹什麼).
好了,基礎的揹包問題就整理到這了.
揹包問題(動態規劃基礎)
dp i j 表示的是要存放第i個物品時,揹包此時的容量為j,揹包內物品的總價值 weight表示物品佔據揹包的體積 dp i j dp i 1 j dp i 1 j weight i value i 狀態轉移方程我們之前看的關於dp的問題,對於每乙個元素的處理方法就是選和不選 我們的狀態轉移方程也...
動態規劃基礎 0 1揹包問題
這種揹包問題是最基礎的一類揹包問題。只要掌握的這種,後面的也就是大同小異了。因此,我在這個問題上將講得非常詳細。0 1揹包問題就是存在乙個揹包有著固定的容積m,物品有自己的質量w和價值c,當然,每乙個物品都只有一件,而且不可拆分。你的任務是 把這些東西裝入這個揹包,使裝入物品的價值最大。首先,貪心演...
動態規劃 揹包問題
給定n個物品,重量是,價值是,包的容量 承重 是w 問,放入哪些物品能使得包內價值最大 1 需要將問題轉化為子問題,通過遞迴實現,且子問題必然與父問題存在關聯 2 定義v i,j 表示為,當item取自前i個items且揹包capacity j 時,揹包問題的最優解,也即最高的價值。3 從前i個it...