POJ1276 多重揹包(01揹包 完全揹包)

2022-07-17 03:57:13 字數 3033 閱讀 6183

多重揹包模板題,給定揹包容量\(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,貨幣的面額同樣理解成重量,那麼這個問題就是乙個多重揹包問題。多重揹包問題可以轉換成完全揹...