動態規劃之徹底揹包詳解

在昨天我已經很詳細的講解過01揹包的動態規劃問題了,今天我講解的是徹底揹包的問題,這是01揹包的詳解:http://www.cnblogs.com/Kalix/p/7617856.htmlhtml

先看問題:在n種物品中選取若干件(同一種物品可屢次選取)放在空間爲v的揹包裏,每種物品的體積爲c1,c2,…,cn,與之相對應的價值爲w1,w2,…,wn.求解怎麼裝物品可以使揹包裏物品總價值最大算法

看完這個問題,你也許會以爲這個不就是01揹包的升級版嗎,其實就是這樣,徹底揹包問題與01揹包問題的區別在於徹底揹包每一件物品的數量都有無限個,而01揹包每件物品數量只有1個數組

因此說與它相關的策略已經不是隻有取和不取這兩種策略了,而是有取0件、取1件、取2件……等等不少種策略優化

若是咱們用和01揹包同樣的狀態,f[i][v]表示前i種物品恰放入一個容量爲v的揹包的最大價值,那咱們應該用k表示當前容量下能夠裝第i種物品的件數,那麼k的範圍應該是0≤k≤v/c[i],
spa

既然要用當前物品i把當前容量裝滿,那須要0≤k≤v/c[i]件,其中k表示件數。htm

下面給出狀態轉移方程:blog

f[i][j] = max{f[i-1][j],f[i-1][j - k * c[i]] + k * w[i]}(0<=k*c[i]<=v)排序

 

貼一段代碼:繼承

 

for (int i = 1; i < n; i++){
   for (int j = 1; j <= v; j++){
     for (int k = 0; k*c[i] <= j; k++){
    if(c[i]<=j)/*若是能放下*/
    f[i][j] = max{f[i][j],f[i-1][j - k * c[i]] + k * w[i]};/*表示前i-1種物品中選取若干件物品放入剩餘空間爲j-k*w[i]的揹包中所能獲得的最大價值加上k件第i種物品的總價值*/

   else/*放不下的話*/    f[i][j]=f[i-1][j]/*繼承前i-1個物品在當前空間大小時的價值*/
      } 
   } 
}

咱們能夠對其進行優化:若是有兩件物品a、b知足c[a]<=c[b]且w[a]>=w[b],則將物品b去掉,不用考慮。由於你能夠用 佔用體積小的物品 獲得 比 佔用體積大的物品還要多的價值,何樂而不爲呢。其實對於徹底揹包,能夠再優化,首先將容量大於v的物品去掉,而後排序計算出容量相同的物品中價值最高的是哪一個,咱們只要價值大的就能夠了。ci

畫一個v=6,c[1]=1 , w[1]=3 ; c[2]=3 , w[2]=10的表格

 i\j

j=0 j=1 j=2 j=3 j=4 j=5 j=6

i=0

0 0 0 0 0 0 0

i=1

0 3 6 9 12 15 18

i=2

0 3 6 10 13 16 20

以上算法能夠理解成:dp每一種物品在不一樣剩餘容量下的最優解,他是以每1種作單位的,每一種物品能夠包含若干個該物品。考慮是否在該種物品中添加一件新的該物品

咱們再進行優化,改變一下dp思路

咱們能夠把把徹底揹包問題轉化爲01揹包問題來解,第i種物品最多選V/c[i]件,因而能夠把第i種物品轉化爲v/c[i]件費用及價值均不變的物品,而後求解這個01揹包問題。

即:將一種物品拆成多件物品。

咱們如今dp每個物品,dp出該種物品在不一樣剩餘容量下的最優解,他是以每1個爲單位的。考慮是否在當前全部物品總數中添加一件新的該物品

咱們用i表明前i種物品,v表明包的最大承重,c[i]是第i種物品消耗的空間、w[i]是第i種物品的價值、f[i,j]是最大價值(從前i種物品取若干件放入有j個剩餘空間的包)。

若是不放那麼f[i][j]=f[i-1][j]

若是肯定放,那麼f[i][j]=f[i][j-c[i]+w[i]],爲何會是f[i][j-c[i]]+w[i]?

由於咱們要考慮的是在當前基礎上添加一件物品i。

就是說若是你放第i種物品,並不牽扯到第i-1種物品,因此無論你放多少件,都要在第i種商品的基礎上操做

因此說遞推式爲:

f[i][j]=max(f[i-1][j],f[i][j-c[i]]+w[i])

該代碼:

	for (int i = 1; i <= n; ++i) {
		for (int j = 1; j <= v; ++j) {
			if (c[i] <= j)
				f[i][j] = max(f[i - 1][j],f[i][j - c[i]] + v[i]);
			else
				f[i][j] = f[i - 1][j];
		}
	}

 咱們能夠繼續優化此算法,能夠用一維數組寫

咱們先回顧01揹包爲何寫1維要逆序?

由於爲了不要使用的子狀態收到影響。

那咱們該如何寫徹底揹包的1維優化呢?

答案是:順序

由於第i種物品一旦出現,原來沒有第i種物品的狀況下可能有一個最優解,如今第i種物品 出現了,而它的加入有可能獲得更優解,因此以前的狀態須要進行改變,故須要正序。

因此說遞推式是這樣子的:

f[j] = max(f[j],f[j-c[i]]+w[i])

貼出代碼:

 

	for (int i = 1; i <= n; ++i) {
		for (int j = w[i]; j <= v; ++j) {
			f[j] = max(f[j], f[j - c[i]] + v[i]);
		}
	}

若是此文寫的有bug,請及時留言!謝謝

本文章原創

Authentic Author : Tranx

2017.10.2  19:18

相關文章
相關標籤/搜索