多重揹包模板題,給定揹包容量\(v\),給定\(n\)種物品,每種物品的個數\(n_i\)、體積\(v_i\)和重量\(w_i\)已知,求揹包能裝下的物品的最大重量。對應本題就是,給定提款的金額cash,給定\(n\)種錢幣,每種錢幣的個數為\(n_i\)、面額\(d_i\)已知,求能兌換的錢幣的最大值。本題中,體積和重量都等於面額。
735 3 4 125 6 5 3 350
633 4 500 30 6 100 1 5 0 1
735 0
0 3 10 100 10 50 10 10
735
6300
0
剛開始的想法是直接轉化為01揹包問題來解,即將第\(i\)種物品換成\(n_i\)件01揹包中的物品,則得到了物品數為\(\sum\)的01揹包問題,直接求解複雜度為\(\mathcal(v\sum)\),測試表明會tle。
需要對以上的樸素想法進行優化。將第\(i\)件物品參考二進位制的思想來拆分,即拆分乘若干件01揹包中的物品,每件物品的係數分別為\(2^0, 2^1, 2^2,\cdots,2^,n_i-2^k+1\),\(k\)是滿足\(n_i-2^k+1>0\)的最大整數。例如,若\(n_i=13\),則可以拆分為係數分別為\(1,2,4,6\)四件物品。拆分的核心出發點就是,原先的\(1\cdots n_i\)之間的任意整數都能用相應的係數組合而成。這樣的話,複雜度就可以降低為\(\mathcal(v\sum \rceil})\)。
另外,若\(n_i\times v_i>v\)時,針對物品\(i\)可以看作完全揹包問題,可以用完全揹包的複雜度為\(\mathcal(nv)\)演算法求解物品\(i\)。
result: tle
#include #include #include int cash, n;
int denominations[10 * 1000 + 5];
int opt[100000 + 5];
int main()
for (int i = 1; i < ptr; i++)
for (int j = cash; j >= denominations[i]; j--)
opt[j] = std::max(opt[j], opt[j - denominations[i]] + denominations[i]);
printf("%d\n", opt[cash]);
} return 0;
}
程式中01揹包和完全揹包都優化了空間複雜度,只需要定義opt
為一維陣列即可。二維陣列opt
到一維陣列opt
的空間優化沒那麼好理解,加一點註解。
01揹包狀態轉移方程:
\[\begin
opt[i][j] = \max(opt[i-1][j], opt[i-1][j-v_i] + w_i)
\end
\]對於zeroonepack
函式的註解:
若外層正處於第i
次迴圈,內層迴圈中,j
逆序減小,計算opt[j]
的值時,opt[j]
、opt[j-cost]
儲存都是i-1
次迴圈的值,即分別對應於(1)式中的\(opt[i-1][j]\)、\(opt[i-1][j-v_i]\)。而如果j
正序增大的話,計算opt[j]
時,opt[j-cost]
已經賦過值,儲存是i
次迴圈的值,與狀態轉移方程不符。
完全揹包狀態轉移方程:
\[\begin
opt[i][j] = \max(opt[i-1][j], opt[i][j-v_i] + w_i)
\end
\]對於completapack
函式的註解:
若外層正處於第i
次迴圈,內層迴圈中,j
正序增大,計算opt[j]
的值時,opt[j]
儲存的是i-1
次迴圈的值,即對應(2)式中的\(opt[i - 1][j]\)。opt[j-cost]
在本次迴圈中已經賦過值,儲存的是第i
次迴圈的值,對應於(2)式中的\(opt[i][j-v_i]\),與狀態轉移方程相符。opt[j] = std::max(opt[j], opt[j - cost] + weight)
這個表示式表示,我正在考慮是否往揹包中加一件物品i
,而\(opt[i][j-v_i]\)中可能已經包含了若干件物品i
,現在考慮是否再增加物品i
,所以這一點上可以反映完全揹包每種物品有無窮多的性質,也正是completepack
正確的原因。
result: 700kb, 32ms
#include #include #include int cash, n;
int opt[100000 + 5];
void completapack(int cost, int weight)
void zeroonepack(int cost, int weight)
void multiplepack(int cost, int weight, int num)
int k = 1;
while (k < num)
zeroonepack(num * cost, num * weight);
return;
}int main()
printf("%d\n", opt[cash]);
} return 0;
}
[1] 揹包問題九講 2.0 poj1276 多重揹包
題意 取款機的問題 有 n 種錢 每種錢有 v i 的價值 每種錢有 w i 張 問給定要取得錢 cash 之後能從取款機最多取多少錢 理解 多重揹包 直接套模板 遞推式 dp i max dp i dp i mul v i mul v i 其中的值根據 定義 如下 include include ...
POJ1276 多重揹包
說到揹包問題,都少不了網上很出名的揹包九講。我也是看了那個以後才知道怎麼做的。多重揹包 就是在0 1揹包的基礎上,有的物品可能有多個,問你怎麼選才能使總價值最大。我們最容易想到的是把相同的物品分開,比如說有n個a1物品 就將它分成 a1 a2 a3 an 然後再用01揹包的方法去解決。不過在此題中,...
POJ 1276 多重揹包問題
題目在 題目大意是說,我現在要從atm中取錢,m atm裡面有若干種貨幣,每一種都有對應的貨幣面額和張數。問現在atm能夠取出來的 小於等於m的最大金額。若將m理解為揹包重量,而每種貨幣的面額理解成 value,貨幣的面額同樣理解成重量,那麼這個問題就是乙個多重揹包問題。多重揹包問題可以轉換成完全揹...