有n個物品,它們有各自的體積和價值,現有給定容量的揹包,如何讓揹包裡裝入的物品具有最大的價值總和?
為方便講解和理解,下面講述的例子均先用具體的數字代入,即:eg:number=4,capacity=8
i(物品編號)12
34w(體積)23
45v(價值)34
56根據動態規劃解題步驟(問題抽象化、建立模型、尋找約束條件、判斷是否滿足最優性原理、找大問題與小問題的遞推關係式、填表、尋找解組成)找出01揹包問題的最優解以及解組成,然後編寫**實現。
動態規劃的原理
動態規劃與分治法類似,都是把大問題拆分成小問題,通過尋找大問題與小問題的遞推關係,解決乙個個小問題,最終達到解決原問題的效果。但不同的是,分治法在子問題和子子問題等上被重複計算了很多次,而動態規劃則具有記憶性,通過填寫表把所有已經解決的子問題答案紀錄下來,在新問題裡需要用到的子問題可以直接提取,避免了重複計算,從而節約了時間,所以在問題滿足最優性原理之後,用動態規劃解決問題的核心就在於填表,表填寫完畢,最優解也就找到。
最優性原理是動態規劃的基礎,最優性原理是指「多階段決策過程的最優決策序列具有這樣的性質:不論初始狀態和初始決策如何,對於前面決策所造成的某一狀態而言,其後各階段的決策序列必須構成最優策略」。
揹包問題的解決過程
在解決問題之前,為描述方便,首先定義一些變數:vi表示第 i 個物品的價值,wi表示第 i 個物品的體積,定義v(i,j):當前揹包容量 j,前 i 個物品最佳組合對應的價值,同時揹包問題抽象化(x1,x2,…,xn,其中 xi 取0或1,表示第 i 個物品選或不選)。
1、建立模型,即求max(v1x1+v2x2+…+vnxn);
2、尋找約束條件,w1x1+w2x2+…+wnxn3、尋找遞推關係式,面對當前商品有兩種可能性:
包的容量比該商品體積小,裝不下,此時的價值與前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);
由此可以得出遞推關係式:
j=w(i) v(i,j)=max{v(i-1,j),v(i-1,j-w(i))+v(i)}
這裡需要解釋一下,為什麼能裝的情況下,需要這樣求解(這才是本問題的關鍵所在!):
可以這麼理解,如果要到達v(i,j)這乙個狀態有幾種方式?
肯定是兩種,第一種是第i件商品沒有裝進去,第二種是第i件商品裝進去了。沒有裝進去很好理解,就是v(i-1,j);裝進去了怎麼理解呢?如果裝進去第i件商品,那麼裝入之前是什麼狀態,肯定是v(i-1,j-w(i))。由於最優性原理(上文講到),v(i-1,j-w(i))就是前面決策造成的一種狀態,後面的決策就要構成最優策略。兩種情況進行比較,得出最優。
4、填表,首先初始化邊界條件,v(0,j)=v(i,0)=0;
5、**填完,最優解即是v(number,capacity)=v(4,8)=10。
為了和之前的動態規劃圖可以進行對比,儘管只有4個商品,但是我們建立的陣列元素由5個。
#include #include #include #include #include #include #include #include #include #include using namespace std;
int w[5] = ; //商品的體積2、3、4、5
int v[5] = ; //商品的價值3、4、5、6
int ba** = 8; //揹包大小
int dp[5][9] = }; //動態規劃表
int item[5];
void findwhat(int i, int j)
else if (j - w[i] >= 0 && dp[i][j] == dp[i - 1][j - w[i]] + v[i])
}}void findmax() }}
void print()
cout << endl;
} //路徑的輸出
for (int i = 1; i < 4; i++)
cout << item[4] << endl;
}int main()
設f[j]表示重量不超過j公斤的最大價值 可得出狀態轉移方程
fj=maxjfj=maxj
**如下
#include "stdafx.h"
#include #include #include #include #include #include #include #include #include #include using namespace std;
int w[5] = ; //商品的體積2、3、4、5
int v[5] = ; //商品的價值3、4、5、6
int ba** = 8; //揹包大小
int dp[5][9] = }; //動態規劃表
int item[5];
int dp[500] = ;
void findwhat(int i, int j)
else
}}void findmax() }}
void print()
cout << endl;
//路徑的輸出
for (int i = 1; i < 4; i++)
cout << item[4] << endl;
}int main()
有n件物品和容量為m的揹包 給出i件物品的重量以及價值 求解讓裝入揹包的物品重量不超過揹包容量 且價值最大
特點 題幹看似與01一樣 但它的特點是每個物品可以無限選用
其實這個題也可以寫二維和一維兩種 但之前已經說過了二維的有一定侷限 所以在此之介紹一維
一維 設f[j]表示重量不超過j公斤的最大價值 可得出狀態轉移方程
fj=maxj
**如下
#include "stdafx.h"
#include #include #include #include #include #include #include #include #include #include using namespace std;
int w[5] = ; //商品的體積2、3、4、5
int v[5] = ; //商品的價值3、4、5、6
int ba** = 8; //揹包大小
int dp[5][9] = }; //動態規劃表
int item[5] = ;
int dp[500] = ;
void findwhat(int i, int j)
i--;
}}void findmax() }}
void print()
cout << endl;
//路徑的輸出
for (int i = 1; i < 4; i++)
cout << item[4] << endl;
}int main()
有n件物品和容量為m的揹包 給出i件物品的重量以及價值 還有數量 求解讓裝入揹包的物品重量不超過揹包容量 且價值最大
特點 它與完全揹包有類似點 特點是每個物品都有了一定的數量
狀態轉移方程為:
f[j]=max
【輸入樣例】
8 2
2 100 4
4 100 2
【輸出樣例】
400**如下
#includeusing namespace std;
int main()
int f[10001];
for(int i=1;i<=n;i++)
for(int j=m;j>=0;j--)
for(int k=0;k<=c[i];k++)
cout
(2)
動態規劃揹包問題 01揹包
問題描述 n種物品,每種乙個。第i種物品的體積為vi,重量為wi。選一些物品裝到容量為c的揹包,使得揹包內物品不超過c的前提下,重量最大。問題分析 宣告乙個f n c 的陣列。f i j 表示把前i件物品都裝到容量為j的揹包所獲得的最大重量。當 j v i 時,揹包容量不足以放下第 i 件物品,f ...
動態規劃 揹包問題 01揹包
有n種物品和乙個容量為v的揹包,每種物品僅用一次。第i件物品的費用是w i 價值是v i 求解將哪些物品裝入揹包可使價值總和最大。例如 n 5,v 10 重量 價值 第乙個物品 10 5 第二個物品 1 4 第三個物品 2 3 第四個物品 3 2 第五個物品 4 1 首先我們考慮貪心策略,選取最大價...
0 1揹包問題(動態規劃)
一 問題描述 有n件物品和乙個容量為v的揹包。第i件物品的費用是c i 價值是w i 求解將哪些物品裝入揹包可使價值總和最大。所謂01揹包,表示每乙個物品只有乙個,要麼裝入,要麼不裝入。二 解決方案 考慮使用動態規劃求解,定義乙個遞迴式 opt i v 表示前i個物品,在揹包容量大小為v的情況下,最...