動態規劃 揹包問題(DP系列)

2021-08-14 22:51:31 字數 3541 閱讀 4308

一、問題描述:有n 個物品,它們有各自的重量和價值,現有給定容量的揹包,如何讓揹包裡裝入的物品具有最大的價值總和?

二、總體思路:根據動態規劃解題步驟(問題抽象化、建立模型、尋找約束條件、判斷是否滿足最優性原理、找大問題與小問題的遞推關係式、填表、尋找解組成)找出01揹包問題的最優解以及解組成,然後編寫**實現;

三、動態規劃的原理及過程:

eg:number=4,capacity=8

i

1

2

3

4

w(體積)2

345v(價值)3

456

1、原理

動態規劃與分治法類似,都是把大問題拆分成小問題,通過尋找大問題與小問題的遞推關係,解決乙個個小問題,最終達到解決原問題的效果。但不同的是,分治法在子問題和子子問題等上被重複計算了很多次,而動態規劃則具有記憶性,通過填寫表把所有已經解決的子問題答案紀錄下來,在新問題裡需要用到的子問題可以直接提取,避免了重複計算,從而節約了時間,所以在問題滿足最優性原理之後,用動態規劃解決問題的核心就在於填表,表填寫完畢,最優解也就找到。

2、過程

a) 把揹包問題抽象化(x1,x2,…,xn,其中 xi 取0或1,表示第 i 個物品選或不選),vi表示第 i 個物品的價值,wi表示第 i 個物品的體積(重量);

b) 建立模型,即求max(v1x1+v2x2+…+vnxn);

c) 約束條件,w1x1+w2x2+…+wnxnd) 定義v(i,j):當前揹包容量 j,前 i 個物品最佳組合對應的價值;

e) 最優性原理是動態規劃的基礎,最優性原理是指「多階段決策過程的最優決策序列具有這樣的性質:不論初始狀態和初始決策如何,對於前面決策所造成的某一狀態而言,其後各階段的決策序列必須構成最優策略」。判斷該問題是否滿足最優性原理,採用反證法證明:

假設(x1,x2,…,xn)是01揹包問題的最優解,則有(x2,x3,…,xn)是其子問題的最優解,

假設(y2,y3,…,yn)是上述問題的子問題最優解,則理應有(v2y2+v3y3+…+vnyn)+v1x1 > (v2x2+v3x3+…+vnxn)+v1x1;

而(v2x2+v3x3+…+vnxn)+v1x1=(v1x1+v2x2+…+vnxn),則有(v2y2+v3y3+…+vnyn)+v1x1 > (v1x1+v2x2+…+vnxn);

該式子說明(x1,y2,y3,…,yn)才是該01揹包問題的最優解,這與最開始的假設(x1,x2,…,xn)是01揹包問題的最優解相矛盾,故01揹包問題滿足最優性原理;

f) 尋找遞推關係式,面對當前商品有兩種可能性:

第一,包的容量比該商品體積小,裝不下,此時的價值與前i-1個的價值是一樣的,即v(i,j)=v(i-1,j);

第二,還有足夠的容量可以裝該商品,但裝了也不一定達到當前最優價值,所以在裝與不裝之間選擇最優的乙個,即v(i,j)=max{ v(i-1,j),v(i-1,j-w(i))+v(i) }

其中v(i-1,j)表示不裝,v(i-1,j-w(i))+v(i) 表示裝了第i個商品,揹包容量減少w(i)但價值增加了v(i);

由此可以得出遞推關係式:

1)j2)j>=w(i)     v(i,j)=max{ v(i-1,j),v(i-1,j-w(i))+v(i) }

g) 填表,首先初始化邊界條件,v(0,j)=v(i,0)=0;

h) 然後一行一行的填表,

1) 如,i=1,j=1,w(1)=2,v(1)=3,有j2) 又如i=1,j=2,w(1)=2,v(1)=3,有j=w(1),故v(1,2)=max{ v(1-1,2),v(1-1,2-w(1))+v(1) }=max{0,0+3}=3;

3) 如此下去,填到最後乙個,i=4,j=8,w(4)=5,v(4)=6,有j>w(4),故v(4,8)=max{ v(4-1,8),v(4-1,8-w(4))+v(4) }=max{9,4+6}=10;所以填完表如下圖:

void findmax()//動態規劃

else//前i-1個物品的最優解與第i個物品的價值之和更大}}

}}

用動態轉移方程可以簡化該**,可以用作模板記住

#include#include#include#includeusing namespace std;

int dp[1000];

int v[1000];

int w[1000]

int main()

for(int i=0;i=w[i];j--)

dp[j]=max(dp[j],dp[j-w[i]]+v[i]);

} cout<

對於揹包問題在前面動態規劃 - 0-1揹包問題的演算法優化已經講到了關於0-1揹包問題的解法,0-1揹包問題是最基本的揹包問題,它的特點是:每一件物品之多只能選擇一件,即在揹包中該物品數量只有0和1兩種情況。

現在擴充套件一下,有乙個容積為v的揹包,同時有n種物品,每種物品均有無數多個,並且每種物品的都有自己的體積和價值。求使用該揹包最多能夠裝的物品價值總和。

這就是完全揹包問題。

如果按照0-1揹包的思路求解該問題,可設當前物品的體積為w,價值為v,考慮到揹包中最多存放v/w件該物品,那麼該物品的可選數量就為v/w件。依次可以對所有的物品進行拆分,最後對拆分的所有物品做0-1揹包即可得到答案。但是,這樣拆分會使物品數量大大增加,其時間複雜度為:o(v*∑n

i=1(v/wi))。

可見,當v較大時每個物品的體積較小時,其時間複雜度會顯著增大。所以將完全揹包問題轉化為0-1揹包問題去解決的方法不可靠。

在0-1揹包的解決演算法中,其中一段**是該演算法的核心演算法,如下:

for(int i=0;i=w[i];j--)

dp[j]=max(dp[j],dp[j-w[i]]+v[i]);

}

在這段**中,之所將j初始化為s,逆序迴圈更新狀態是為了保證在更新dp[j]時,dp[j-w[i]]的狀態尚未因為本次更新而發生改變,即等價於由

dp[i-1][j-w[i]]轉移得到dp[i][j]。保證了更新dp[j]時,dp[j-w[i]]是沒有放入物品i時的資料dp[i-1][

j-w[i]]。

在解決完全揹包問題時,可以借鑑這個思路。在完全揹包中,每個物品可以被無限次選擇,那麼狀態dp[i][j]恰好可以由可能已經放入物品i的狀態dp[i][

j-w[i]]轉移而來。可以將上面的**改寫如下:

for(int i=0;i

DP 動態規劃 揹包問題

將乙個容量為v的揹包,物品有兩個屬性,乙個w和乙個v表示體積和屬性值。每種物品只有乙個。要求裝下盡可能多,求最大價值。轉移狀態方程 dp j max dp dp j list i w list i v,dp j 1 include includeusing namespace std struct ...

動態規劃(DP)揹包問題

dp做題的步驟 1.確定狀態變數 dp i dp i j 的含義 2.確定狀態轉移方程 3.確定邊界條件 4.確定遞推順序 題目1 01揹包 有n個重量和價值分別為w和v的物品。從這些物品中挑選總重量不超過w的物品。求所有挑選方案中價值總和的最大值 思路 dp i 1 j 表示從前i個物品中選出總重...

DP動態規劃 揹包問題

具體例子 有n個重量和價值分別為wi,vi的物品,從這些物品中挑選出總重量不超過w的物品,求所有挑選方案中價值總和的最大值。例如 n 4 w,v w 5 dp思想 求出狀態轉移方程,也就是求出遞推式。首先將問題一般化 解決此問題需要2個一維陣列,和1個二維陣列 w i 表示第i個物品的重量,下標從0...