有一類動態規劃可解的問題,它可以描述稱為若干有序的階段,且每個階段的狀態只和上乙個階段的狀態有關,一般把這類問題稱為多階段規劃問題。
01揹包問題描述如下:
有 n 件物品,每件物品的重量為 w[i],價值為 c[i]。現有乙個容量為 v 的揹包,問如何選取物品放入揹包,使得揹包內物品的總價值最大。其中每種物品都只有一件。如果採取暴力搜尋的方法,每件物品都有兩種選擇,因此 n 件物品就有 2n,而o( 2n) 的時間複雜度完全是不能接受的。而使用動態規劃可以將複雜度將為 o(nv)。例子:5 8 // 5 件物品, 容量為 8
3 5 1 2 2 // w[i] 重量
4 5 2 1 3 // c[i] 價值
令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的五件物品,它們的...