1、 01 揹包
(1)題目:
有N件物品和一個容量爲V 的揹包。放入第i件物品耗費的空間是Ci,得到 的價值是Wi。求解將哪些物品裝入揹包可使價值總和最大。
(2)分析:
這是最簡單的揹包問題,一個物品只有放與不放兩種情況
(3)狀態轉移方程:
定義F[ i , j ] 爲前i個物品正好放入容量爲j的揹包中所獲得的最大價值, 轉移方程 F[ i, j ] = max{F[ i -1, j ] , F[ i-1 , j - w[i] ] + v[i] (j<V)}
(4)代碼
f[0][0] = 0;
for (int i=1;i<=n;i++)
for (int j=w[i];j<=V;j--){
f[i][j]=max(f[i-1][j],f[i-1][j-w[i]]+v[i]);
}
(5)空間優化
void ZeroOnePage(int C,int v);
for (int i=1;i<=n;i++)
for (int j=V;j>=w;j--){
f[j]=max(f[j],f[j-w]+v)
}
抽象成一個函數ZeroOnePage
void ZeroOnePage(int C,int W)
{
for (int j=V;j>=C;j--)
F[j] = max(F[j],F[j-C]+W);
}
爲什麼第二重循環要逆向? 如果仍未正向,F[ i, j ] = max{F[ i -1, j ] , F[ i-1 , j - w[i] ] + v[i] (j<V)},一維數組,f([i - 1])[j]已經被更改了
(6)初始化揹包的細節問題
一般在這個地方有兩種類型,一種題目要求恰好裝滿,另一種情況則沒有要求,,,對於第一種情況的初始化,F[0 , 0] = 0 外,其餘的都應該初始化爲負無窮;對於第二種情況,全部初始爲0便可
題目:見vijos
(1)、題目
有N種物品和一個容量爲V 的揹包,每種物品都有無限件可用。放入第i種 物品的耗費的空間是Ci,得到的價值是Wi。求解:將哪些物品裝入揹包,可使 這些物品的耗費的空間總和不超過揹包容量,且價值總和最大
(2)、分析
類似於0 1揹包的,只是所不同的是物品可以取無限次,將0 1 揹包取或不取兩種狀態變成不取或取0,1,2,3.。。。V/Ci件
仍按照0 1 揹包的思路定義F[ i , j ] 爲前i個物品正好放入容量爲j的揹包中所獲得的最大價值, 轉移方程 F[ i, j ] = max{F[ i -1, j ] , F[ i-1 , j - k*w[i] ] + k*v[i] (k*w[i]<V)}
時間複雜度爲 O(VN*V/Ci),複雜度過於大,需想辦法優化
(3)優化1
若兩件物品i,j 滿足 Ci<=Cj && Wi>=Wj 則將物品j直接去掉,不用考慮。
另外針對揹包也可以這麼優化,做一次計數排序,計算出費用相同價值最高的物品
(4)轉化爲 0 1 揹包求解
因爲每件物品最多選擇 [V/Ci]件,可以構造一個[V/Ci]件價值和費用的0 1 揹包,但這樣的時間複雜度不變,但不乏爲一種想法
高效想法:考慮二進制,把物品拆成Ci*2^k,Wi*2^k,其中k是取遍Ci*2^k<V的所有負整數,,,時間複雜度爲O(log(V/C)*N),不失爲一種好的優化方式
(5)O(VN)的解法(加空間優化)
void CompletePake(int w,int v);
for (int i=1;i<=n;i++)
for (int j=w[i];j<=V;j--){
f[j]=max(f[j],f[j-w[i]]+v[i]);
}
抽象成一個函數CompletePage
void CompletePage(int C,int W)
{
for (int j=C;j<=V;j++)
F[j] = max(Fj],F[j-C]+W);
}
可以思考一下完全揹包爲什麼順序循環?
3、多重揹包
(1)、題目
有N種物品和一個容量爲V 的揹包。第i種物品最多有Mi件可用,每件耗費 的空間是Ci,價值是Wi。求解將哪些物品裝入揹包可使這些物品的耗費的空間 總和不超過揹包容量,且價值總和最大。
(2)、分析:
與完全揹包類似,只是把每個物品可選無限加了一個限定,轉移方程F[i , j ] = max{F[ i-1, j ] , F[i-1,j-k*w[i]]+k*v[i] | k<=Mi }
(3)、優化
轉化爲 0 1 揹包時間複雜度不變,暫不考慮,考慮二進制思想,把Mi件物品分成係數分別爲2^1,2^2。。。2^k,Mi-2^k+1,(k爲滿足Mi-2^k+1>0的最大整數)
抽象成一個函數MultiplePage
void Multiple(int i,int C,int W,int M)
{
if (C*M <= V)
{
CompletePage(C,W);
return;
}
k = 1;
while (k < M)
{
ZeroOnePage(k*C,k*W);
M = M-k;
k <<= 1;
}
ZeroOnePage(M*C,M*W);
}
(4)O(VN)的單調隊列優化,未完待續ing
4、混合揹包
(1)、問題
如果將前面1、2、3中的三種揹包問題混合起來。也就是說,有的物品只可 以取一次(01揹包),有的物品可以取無限次(完全揹包),有的物品可以取 的次數有一個上限(多重揹包)。應該怎麼求解呢
(2)、分析:
如果真正學會了前面三種揹包,那混合揹包完全就是前三種揹包的一個聯合體而已
for i = 1 to N
if 第i件物品屬於01揹包
ZeroOnePack(Ci,Wi)
else if 第i件物品屬於完全揹包
CompletePack(Ci,Wi)
else if 第i件物品屬於多重揹包
MultiplePack(Ci,Wi,Ni)
小結:(轉自揹包九講)有人說,困難的題目都是由簡單的題目疊加而來的。這句話是否公理暫且存之不論,但他在本講中得到了充分的體現。本來0 1 揹包,完全揹包,多重
揹包都不是難題,但是將他們簡單的組合起來以後就得到了一道一定能嚇到不少人的題目。但只要基礎紮實,領會三種揹包問題的思想,就可以把複雜的問題拆分成簡單的問題
來解決(藉此與大家共勉)