標籤:0-1背包 動態規劃 演算法
問題描述:給定n種物品和一背包。物品i的重量是w[i],其價值為v[i],背包的容量為C。問應如何選擇裝入背包的物品,使得裝入背包中物品的總價值最大?
分析:對於一種物品,要麼裝入背包,要麼不裝。所以對於一種物品的裝入狀態可以取0和1。設物品i的裝入狀態為xi,xi∈ (0,1),此問題稱為0-1背包問題。
資料:物品個數n=5,物品重量w[5]={2,2,6,5,4},物品價值v[5]={6,3,5,4,6},總重量c=10。背包的最大容量為10,那麼在設定數組m大小時,可以設行列值為5和10,那麼,對於m(i,j)就表示可選物品為i到n且背包容量為j(總重量)時背包中所放物品的最大價值。看下面這個表格即為動態規劃法解0-1背包問題的過程:
表格中灰色的為序號:列的1~5表示5個物品,橫向的1~10表示背包的容量,綠色的列表示相對應物品的的重量,淺藍色的一列表示相對應物品的價值。假設建立一個5*10的數組對應圖片中的棕色部分,棕色部分即為求解的過程,具體求解過程如下:
圖上所示的求解過程是從下往上求解,也就是說先分析第5個物品,最後分析第一個物品。當背包中為空白的時候,考慮第5個物品,當背包容量為1, 2, 3的時候這個背包都是裝不下物品5的,因為物品5的重量是4,因此對應的當背包容量為1, 3, 3的時候背包裡面東西的價值(棕色表格裡面的值)也是為0,當背包容量大於等於4的時候背包可以放下物品5,所以背包裡面東西的價值就是6(因為這裡先只考慮只有一件物品5的時候),到此只有物品5的情況已經分析完畢;
接下來分析同時擁有物品5和物品4的情況,當背包容量為1, 2, 3的時候,背包裡面既放不下4物品也放不下5物品,所以背包裡面物品的價值為0,當背包容量大於等於4的時候,至少可以放下物品5了,這個時候就要取捨了,到底是將物品5放進去價值大還是將物品4放進去價值大,當背包容量為4的時候,只能放進去物品5,價值為6,當背包容量為5的時候如果選擇房屋物品4,那麼剩餘的背包容量為0,尋找背包重量為0的列(在前面步驟已經填充過的部分,這裡只填充了第5行第1列的位置),找這一列的最大值為0,所以選擇放物品4的時候背包價值最大為4<不放物品4(剩餘背包容量為5,尋找背包容量5對應的填充過的部分,其最大值為6)時候的6,所以在背包容量為5的時候的最優值是放物品5而不放物品4,一直分析到背包容量為9的時候當選擇放入物品4的時候,剩餘背包容量為4,再尋找背包容量為4時候已經填充過的部分(即最後一行),可以查得最大值為6,所以這個時候選擇放入物品4可以獲得的最大價值為10。...
中間的過程都是如此,這裡再分析一下最後一個物品的放置,背包容量為2的時候,如果放入物品1,獲得的價值為6,剩餘背包容量為0,剩餘背包容量獲得的最大價值為0,所以6+0=6為最大價值,如果不放入1物品,剩餘背包容量為2,剩餘背包容量可以獲得的最大價值為3,所以最優的時候應該是放入物品1;背包容量為3的時候,若放入1物品,獲得價值6,剩餘背包容量1可以獲得的最大價值為0,所以放入物品1可以獲得的最大價值為6,如果不放入物品1,那麼剩餘背包容量3可以獲得的最大價值(在已經填充過的部分尋找)為3,所以這時也是放入物品1為最優......當背包容量為8的時候放入物品1獲得價值6,剩餘背包容量6可以獲得最大價值為9,放入物品1的最大價值為6+9=15,如果不放入物品1,剩餘背包容量8可以獲得的最大價值為9,所以此時也是放入物品1最優。
總結:通過上面的分析過程,可以歸結為:先考慮這個物品放入的時候可以獲得的最大價值(這個物品的價值+剩餘背包(背包總容量-該物品重量)容量可以獲得的最大價值),再考慮不放入這個物品的時候可以獲得的最大價值(剩餘背包容量(此時就是背包總重量)可以獲得的最大價值),然後將2者進行比較,那種結果的價值大就將哪種結果的價值儲存下來,依次類推。
#include<stdio.h> const int c = 10; //背包的容量 const int w[] = {2,2,6,5,4};//物品的重量 const int v[] = {6,3,5,4,6};//物品對應的待加 const int n = sizeof(w)/sizeof(w[0]) - 1 ; //n為物品的個數 int x[n]; void package0_1(int m[][10],const int w[],const int v[],const int n)//n代表物品的個數 { int i, j;/*********************************最後一個物品單獨放置*********************************/ for(j = 1; j <= c; j++) { if(j < w[n]) /*背包容量<最後一個物品的重量時*/ m[n][j-1] = 0; else /*背包能夠放下最後一個物品時*/ m[n][j-1] = v[n]; } /*********************************放置前n-1個物品*********************************/ for(i = n-1; i >= 0; i--) for(j = 1; j <= c; j++) { if(j < w[i]) m[i][j-1] = m[i+1][j-1];//如果j < w[i]則,當前位置就不能放置,它等於上一個位置的值 else //否則,就比較到底是放置之後的值大,還是不放置的值大,選擇其中較大者 m[i][j-1] = m[i+1][j-1] > m[i+1][j-1-w[i]]+v[i] ? m[i+1][j-1] : m[i+1][j-1-w[i]]+v[i];}} void answer(int m[][10],const int n) { int j = c-1; /*i = 0, j= c-1座標上存放著背包容量為c時的最大價值*/ int i; for(i = 0; i < n; i++) if(m[i][j] == m[i+1][j]) x[i] = 0; else { x[i] = 1; /*如果當前物品放入了背包*/j = j - w[i]; /*重新計算背包剩餘容量,以計算在取最優時其他物品的取捨情況*/ }i -= 1; x[n] = m[i][j] ? 1 : 0; } int main() { int m[6][10]={0}; int i, j; package0_1(m,w,v,n); for(i = 0; i <= 4; i++) { for(j = 0; j < 10; j++) printf("%3d ",m[i][j]); printf("\n"); } answer(m,n); printf("The best answer is:\n"); for(i = 0; i < 5; i++) printf("%d ", x[i]); printf("\n"); return 0; }
程式運行結果如下: