完全揹包 2016 5 8

2022-04-08 03:20:09 字數 2829 閱讀 7420

有 n 種物品和乙個容量為 v 的揹包,每種物品都有無限件可用

放入第 i 種物品的耗費的空間是 ci,得到的價值是 wi

求解:將哪些物品裝入揹包,可使這些物品的耗費的空間總和不超過揹包容量,且價值總和最大

這個問題非常類似於01揹包問題,所不同的是每種物品有無限件

也就是從每種物品的角度考慮,與它相關的策略已並非取或不取兩種

而是有取 0 件、取 1 件、取 2 件……直至取 ?v / ci? 件等很多種

如果仍然按照解01揹包時的思路,令 dp[i, v] 表示前i種物品恰放入乙個容量為 v 的揹包的最大權值

仍然可以按照每種物品不同的策略寫出狀態轉移方程

像這樣:

dp[i, v] = max

這跟01揹包問題一樣有o(v * n)個狀態需要求解

但求解每個狀態的時間已經不是常數了,求解狀態 dp[i, v] 的時間是 o( v / ci)

總的複雜度可以認為是 o(n * v * σv/ci ),是比較大的

將01揹包問題的基本思路加以改進,得到了這樣乙個清晰的方法

這說明01揹包問題的方程的確是很重要,可以推及其它型別的揹包問題

但我們還是要試圖改進這個複雜度

若兩件物品 i、 j 滿足 ci ≤ cj 且 wi ≥ wj ,則將可以將物品 j 直接去掉,不用考慮

這個優化的正確性是顯然的:

任何情況下都可將價值小耗費高的j換成物美價廉的 i,得到的方案至少不會更差

對於隨機生成的資料,這個方法往往會大大減少物品的件數,從而加快速度

然而這個並不能改善最壞情況的複雜度,因為有可能特別設計的資料可以一件物品也去不掉

這個優化可以簡單的o(n ^ 2)地實現,一般都可以承受

另外,針對揹包問題而言,比較不錯的一種方法是:

首先將費用大於v 的物品去掉,然後使用類似計數排序的做法,計算出費用相同的物品中價值最高的是哪個

可以o(v + n)地完成這個優化

考慮到第 i 種物品最多選 v / ci 件

於是可以把第 i 種物品轉化為 v / ci 件費用及價值均不變的物品,然後求解這個01揹包問題

這樣的做法完全沒有改進時間複雜度

但這種方法也指明了將完全揹包問題轉化為01揹包問題的思路:

將一種物品拆成多件只能選 0 件或 1 件的01揹包中的物品

更高效的轉化方法是:

把第 i 種物品拆成費用為 ci * 2^k、價值為 wi * 2^k 的若干件物品,其中 k 取遍滿足 ci * 2^k ≤ v 的非負整數

這是二進位制的思想

因為,不管最優策略選幾件第 i 種物品,其件數寫成二進位制後,總可以表示成若干個 2^k 件物品的和

這樣一來就把每種物品拆成 o(log(v / ci) ) 件物品,是乙個很大的改進

這個演算法使用一維陣列,先看偽**:

dp[0..v ] = 0

for i = 1 to n

for v = ci to v

dp[v] = max(dp[v], dp[v ? ci] + wi)

你會發現,這個與01揹包問題的**只有v的迴圈次序不同而已

為什麼這個演算法就可行呢?

首先想想為什麼01揹包中要按照 v 遞減的次序來迴圈

讓v遞減是為了保證第 i 次迴圈中的狀態 dp[i, v]是由狀態 dp[i ? 1, v ? ci]遞推而來

換句話說,這正是為了保證每件物品只選一次

保證在考慮「選入第 i 件物品」這件策略時,依據的是乙個絕無已經選入第 i 件物品的子結果 dp[i ?1, v ? ci]

而現在完全揹包的特點恰是每種物品可選無限件

所以在考慮「加選一件第 i 種物品」這種策略時,卻正需要乙個可能已選入第 i 種物品的子結果 dp[i, v ? ci]

所以就可以並且必須採用 v 遞增的順序迴圈

這就是這個簡單的程式為何成立的道理

值得一提的是,上面的偽**中兩層for迴圈的次序可以顛倒

這個結論有可能會帶來演算法時間常數上的優化

這個演算法也可以由另外的思路得出

例如,將基本思路中求解 dp[i, v ? ci]的狀態轉移方程顯式地寫出來

代入原方程中,會發現該方程可以等價地變形成這種形式:

dp[i, v] = max(dp[i ? 1, v], dp[i, v ? ci] + wi)

將這個方程用一維陣列實現,便得到了上面的偽**

最後抽象出處理一件完全揹包類物品的過程偽**:

def completepack(dp, c, w)

for v = c to v

dp[v] = max

完全揹包問題也是乙個相當基礎的揹包問題,它有兩個狀態轉移方程

希望能夠對這兩個狀態轉移方程都仔細地體會

不僅記住,也要弄明白它們是怎麼得出來的,最好能夠自己想一種得到這些方程的方法

事實上,對每一道動態規劃題目都思考其方程的意義以及如何得來

是加深對動態規劃的理解、提高動態規劃功力的好方法

#include

#include

#include

using namespace std;

const int inf = 0x3f3f3f3f;

const int maxn = 2000 + 10;

int c[maxn];

int w[maxn];

int dp[50010];

int main()

dp[0] = 0;

for (int i = 1; i <= v; ++i)

for (int i = 1; i <= m; ++i)

}if (dp[v] >= 0) else

}return 0;

}

01揹包,完全揹包

動態規劃 動態規劃的核心是狀態以及狀態轉移方程。需要定義乙個 i,j 狀態以及該狀態的指標函式d i,j 01揹包 有n種物品,每種只有乙個,第i件物品的體積為vi質量為wi。選一些物品裝到體積為c的揹包中,使其體積不超過c的前提下重量最大。namevw abcd e 子問題定義 dp i j 表示...

(揹包二)完全揹包

public class beibaocomplete int weight int capacity 8 int value int weight int capacity 12 int result packagecomplete value,weight,capacity system.out...

揹包問題(完全揹包)

1.矩陣鏈乘法 2.投資組合問題 3.完全揹包問題 4.01揹包問題 5.最長公共子串行 乙個揹包,可以放入n種物品,物品j的重量和價值分別為,如果揹包的最大重量限制是b,怎麼樣選擇放入揹包的物品以使得揹包的總價值最大?組合優化問題,設表示裝入揹包的第j個物品的數量,解可以表示為。那麼目標函式和約束...