動態規劃 01揹包 完全揹包

2021-10-04 07:13:19 字數 4425 閱讀 3366

有一類動態規劃可解的問題,它可以描述稱為若干有序的階段,且每個階段的狀態只和上乙個階段的狀態有關,一般把這類問題稱為多階段規劃問題。

01揹包問題描述如下:

有 n 件物品,每件物品的重量為 w[i],價值為 c[i]。現有乙個容量為 v 的揹包,問如何選取物品放入揹包,使得揹包內物品的總價值最大。其中每種物品都只有一件。

例子:5 8 // 5 件物品, 容量為 8

3 5 1 2 2 // w[i] 重量

4 5 2 1 3 // c[i] 價值

如果採取暴力搜尋的方法,每件物品都有兩種選擇,因此 n 件物品就有 2n,而o( 2n) 的時間複雜度完全是不能接受的。而使用動態規劃可以將複雜度將為 o(nv)。

dp[i][v]表示前 i 件物品恰好裝入容量為 v 的揹包中所能獲得的最大價值。那怎麼求解dp[i][v]呢?

考慮第 i 件物品的選擇策略,有兩種策略:

由於只有這兩種策略,所以狀態轉移方程為

dp[i][v] = max

由於dp[i][v]只與之前的狀態dp[i-1]有關,所以可以列舉 i 從 1 到 n,v 從 0 到 v,通過邊界dp[0][v] = 0(即前 0 件物品放入任何容量 v 的揹包中都只能獲得價值 0)就可以把整個 dp 陣列遞推出來。而dp[i][v]表示的恰好為 v 的情況,所以需要列舉dp[n][v]取其最大值。

因此可以寫出**:

for

(int i =

1; i <= n; i++

)}

時間複雜度和空間複雜度都是 o(nv),時間複雜度不能再優化,但空間複雜度還可以優化。

注意到狀態轉移方程中計算dp[i][v]總是需要dp[i-1][v]左側部分的資料(即正上方和左上方的資料),且當計算dp[i+1]時,dp[i-1]的資料又用不到了(只需要用dp[i]),所以我們可以直接開乙個一維陣列dp[v]!列舉方向從 1 到 n,v 從 v 到 0 (逆序),這樣,狀態轉移方程為:

dp[v] = max

[外鏈轉存失敗,源站可能有防盜煉機制,建議將儲存下來直接上傳(img-kje9kwb6-1584931877976)(./img/01bag.png)]

可以這樣理解,dp[i][v]左上角的資料和dp[i][v]右邊的資料放在同乙個陣列裡面,每次計算出乙個dp[i][v],將相當於把dp[i-1][v]覆蓋掉,因為之後的計算不需要再用到了。我們把這種技巧稱為滾動陣列

**如下:

for

(int i =

1; i <= n; i++

)}

這樣就可以用一維陣列解決了,空間複雜度為 o(v)。

特別說明:如果用二維陣列存放,v 的列舉順序是順序還是逆序無所謂,如果用到一維陣列,v 的列舉就必須是逆序!

完整求解 01 揹包**如下:

#include

#include

#include

using

namespace std;

const

int maxn =

1010

;int c, n;

// 最大報銷錢數,菜品數量

int v[maxn]

, p[maxn]

;// 菜的評價分數,菜的**

int dp[maxn]

;// dp陣列

intmax

(int a,

int b)

intmain()

fill

(dp, dp + maxn,0)

;// 初始化為 0

for(

int i =

0; i < n; i++)}

printf

("%d\n"

, dp[c]);

}return0;

}/*author: veeupup

點菜問題,01 揹包

input:

90 4

20 25

30 20

40 50

10 18

40 2

25 30

10 8

output:

9538

*/

01 揹包中的每個物品都可以看作乙個階段,這個階段的狀態有dp[i][0] ~ dp[i][v],它們均由上乙個階段的狀態得到。事實上,對於能夠劃分階段的問題來說,都可以嘗試把階段作為狀態的一維,這可以方便的得到滿足無後效性的的狀態。如果當前的設計不滿足無後效性,那麼不妨把狀態進行公升維,即增加一維或若干維來表示相應的資訊,這樣可能就滿足無後效性了。

完全揹包問題描述如下:

有 n 中物品,每種物品的單件重量為 w[i],價值為 c[i]。現在有乙個容量為 v 的揹包,問如何選取物品放入揹包,使得揹包內物品的總價值最大。每件物品都有無窮件。

和 01 揹包唯一不同的地方就在於物品有了無數件,同樣使用動態規劃:

設定乙個二維陣列,dp[i][v]表示前 i 件物品恰好裝入容量為 v 的揹包中所能獲得的最大價值。陣列dp[n][m]的值就是完全揹包問題的解。

和 0-1 揹包一樣,只考慮第 i 件物品時,可將情況分為是否放入第 i 件物品兩種情況:

所以狀態轉移方程為 :

dp[i][j] = max

邊界:dp[0][v] = 0

其實唯一的區別就在於 max 的第二個引數換成了 dp[i] 而不是 dp[i-1],所以同樣可以寫成一維形式:

dp[v] = max

寫成一維形式之後和 01 揹包完全相同,唯一的區別在於此處是正向列舉,而 01 揹包中的一維形式必須是逆向列舉,完全揹包的一維形式**如下:

for

(int i =

1; i <= n; i++

)}

正向列舉是因為求解dp[i][v]總是需要它左邊的dp[i][v-w[i]]和它上方的dp[i-1][v],顯然如果 v 從小到大列舉,dp[i][v-w[i]]就總是計算出的結果,而計算出dp[i][v]之後就再也用不到dp[i-1][v]了,所以必須正向列舉。

[外鏈轉存失敗,源站可能有防盜煉機制,建議將儲存下來直接上傳(img-xvwjahjl-1584931877977)(./img/wanquanbag.png)]

完全揹包**如下:

/*

author: veeupup

點菜問題,01 揹包

input:

90 4

20 25

30 20

40 50

10 18

40 2

25 30

10 8

output:

9538

*/#include

#include

#include

using

namespace std;

const

int maxn =

1010

;int c, n;

// 最大報銷錢數,菜品數量

int v[maxn]

, p[maxn]

;// 菜的評價分數,菜的**

int dp[maxn]

;// dp陣列

intmax

(int a,

int b)

intmain()

fill

(dp, dp + maxn,0)

;// 初始化為 0

for(

int i =

0; i < n; i++)}

printf

("%d\n"

, dp[c]);

}return0;

}

搞懂 01 這些經典的動態規劃問題,就能夠理解動態規劃的意義所在了。

動態規劃 揹包問題(01揹包 完全揹包)

揹包問題 多種物品 重量不同 價值不同 你可以取最多重量不超過w的物品,問最大價值為多少?01揹包 指的是 有n個物品 每個物品的重量為w i 價值為v i 每個物品只有乙個 所有面臨這些物品只有兩種結果 1 拿這件物品,揹包容量減去w i 此時的價值增加v i 2 不拿這件物品,揹包容量不變,最大...

總結 動態規劃 01揹包 完全揹包

前一段時間我學習了一下動態規劃裡面的01揹包和完全揹包,自己做了一些總結與大家分享一下,希望對你們有幫助 首先來說一下什麼是動態規劃 動態規劃就是把大問題拆分成小問題,通過尋找大問題與小問題的遞推關係,解決乙個個小問題,最終達到解決原問題的效果。通過填寫表把所有已經解決的子問題答案紀錄下來,動態規劃...

動態規劃之01揹包,完全揹包,多重揹包

01揹包問題,是用來介紹動態規劃演算法最經典的例子。f i,j 表示在前i件物品中選擇若干件放在承重為 j 的揹包中,可以取得的最大價值。pi表示第i件物品的價值。決策 為了揹包中物品總價值最大化,第 i件物品應該放入揹包中嗎 0 1的選擇 題目描述 有編號分別為a,b,c,d,e的五件物品,它們的...