動態規劃_01揹包

0-1 揹包問題:給定 n 種物品和一個容量爲 C 的揹包,物品 i 的重量是 wi,其價值爲 vi 。ios

問:應該如何選擇裝入揹包的物品,使得裝入揹包中的物品的總價值最大?算法

 

分析一波,面對每一個物品,咱們只有選擇拿取或者不拿兩種選擇,不能選擇裝入某物品的一部分,也不能裝入同一物品屢次。數組

 

解決辦法:聲明一個 大小爲  m[n][c] 的二維數組,m[ i ][ j ] 表示 在面對第 i 件物品,且揹包容量爲  j 時所能得到的最大價值 ,那麼咱們能夠很容易分析得出 m[i][j] 的計算方法,spa

(1). j < w[i] 的狀況,這時候揹包容量不足以放下第 i 件物品,只能選擇不拿.net

m[ i ][ j ] = m[ i-1 ][ j ]blog

(2). j>=w[i] 的狀況,這時揹包容量能夠放下第 i 件物品,咱們就要考慮拿這件物品是否能獲取更大的價值。ip

若是拿取,m[ i ][ j ]=m[ i-1 ][ j-w[ i ] ] + v[ i ]。 這裏的m[ i-1 ][ j-w[ i ] ]指的就是考慮了i-1件物品,揹包容量爲j-w[i]時的最大價值,也是至關於爲第i件物品騰出了w[i]的空間。ci

若是不拿,m[ i ][ j ] = m[ i-1 ][ j ] , 同(1)get

到底是拿仍是不拿,天然是比較這兩種狀況那種價值最大。string

 

由此能夠獲得狀態轉移方程:

  1. if(j>=w[i])  
  2.     m[i][j]=max(m[i-1][j],m[i-1][j-w[i]]+v[i]);  
  3. else  
  4.     m[i][j]=m[i-1][j];  

 

例:0-1揹包問題。在使用動態規劃算法求解0-1揹包問題時,使用二維數組m[i][j]存儲揹包剩餘容量爲j,可選物品爲i、i+一、……、n時0-1揹包問題的最優值。繪製

價值數組v = {8, 10, 6, 3, 7, 2},

重量數組w = {4, 6, 2, 2, 5, 1},

揹包容量C = 12時對應的m[i][j]數組。

0 1 2 3 4 5 6 7 8 9 10 11 12
1 0 0 0 8 8 8 8 8 8 8 8 8
2 0 0 0 8 8 10 10 10 10 18 18 18
3 0 6 6 8 8 14 14 16 16 18 18 24
4 0 6 6 9 9 14 14 17 17 19 19 24
5 0 6 6 9 9 14 14 17 17 19 21 24
6 2 6 8 9 11 14 16 17 19 19 21 24

(第一行和第一列爲序號,其數值爲0)
如m[2][6],在面對第二件物品,揹包容量爲6時咱們能夠選擇不拿,那麼得到價值僅爲第一件物品的價值8,若是拿,就要把第一件物品拿出來,放第二件物品,價值10,那咱們固然是選擇拿。m[2][6]=m[1][0]+10=0+10=10;依次類推,獲得m[6][12]就是考慮全部物品,揹包容量爲C時的最大價值。

  1. #include <iostream>  
  2. #include <cstring>  
  3. using namespace std;  
  4.   
  5.   
  6. const int N=15;  
  7.   
  8.   
  9. int main()  
  10. {  
  11.     int v[N]={0,8,10,6,3,7,2};  
  12.     int w[N]={0,4,6,2,2,5,1};  
  13.   
  14.   
  15.     int m[N][N];  
  16.     int n=6,c=12;  
  17.     memset(m,0,sizeof(m));  
  18.     for(int i=1;i<=n;i++)  
  19.     {  
  20.         for(int j=1;j<=c;j++)  
  21.         {  
  22.             if(j>=w[i])  
  23.                 m[i][j]=max(m[i-1][j],m[i-1][j-w[i]]+v[i]);  
  24.   
  25.   
  26.             else  
  27.                 m[i][j]=m[i-1][j];  
  28.         }  
  29.     }  
  30.   
  31.   
  32.     for(int i=1;i<=n;i++)  
  33.     {  
  34.         for(int j=1;j<=c;j++)  
  35.         {  
  36.             cout<<m[i][j]<<' ';  
  37.         }  
  38.         cout<<endl;  
  39.     }  
  40.   
  41.   
  42.     return 0;  
  43. }  

 

到這一步,能夠肯定的是可能得到的最大價值,可是咱們並不清楚具體選擇哪幾樣物品能得到最大價值。

另起一個 x[ ] 數組,x[i]=0表示不拿,x[i]=1表示拿。

m[n][c]爲最優值,若是m[n][c]=m[n-1][c] ,說明有沒有第n件物品都同樣,則x[n]=0 ; 不然 x[n]=1。當x[n]=0時,由x[n-1][c]繼續構造最優解;當x[n]=1時,則由x[n-1][c-w[i]]繼續構造最優解。以此類推,可構造出全部的最優解。(這段全抄算法書,實在不知道咋解釋啊。。)

  1. void traceback()  
  2. {  
  3.     for(int i=n;i>1;i--)  
  4.     {  
  5.         if(m[i][c]==m[i-1][c])  
  6.             x[i]=0;  
  7.         else  
  8.         {  
  9.             x[i]=1;  
  10.             c-=w[i];  
  11.         }  
  12.     }  
  13.     x[1]=(m[1][c]>0)?1:0;  
  14. }  


例:

某工廠預計明年有A、B、C、D四個新建項目,每一個項目的投資額Wk及其投資後的收益Vk以下表所示,投資總額爲30萬元,如何選擇項目才能使總收益最大?

 

Project

Wk

Vk

A

15

12

B

10

8

C

12

9

D

8

5

結合前面兩段代碼

  1. #include <iostream>  
  2. #include <cstring>  
  3. using namespace std;  
  4.   
  5. const int N=150;  
  6.   
  7. int v[N]={0,12,8,9,5};  
  8. int w[N]={0,15,10,12,8};  
  9. int x[N];  
  10. int m[N][N];  
  11. int c=30;  
  12. int n=4;  
  13. void traceback()  
  14. {  
  15.     for(int i=n;i>1;i--)  
  16.     {  
  17.         if(m[i][c]==m[i-1][c])  
  18.             x[i]=0;  
  19.         else  
  20.         {  
  21.             x[i]=1;  
  22.             c-=w[i];  
  23.         }  
  24.     }  
  25.     x[1]=(m[1][c]>0)?1:0;  
  26. }  
  27.   
  28. int main()  
  29. {  
  30.   
  31.   
  32.     memset(m,0,sizeof(m));  
  33.     for(int i=1;i<=n;i++)  
  34.     {  
  35.         for(int j=1;j<=c;j++)  
  36.         {  
  37.             if(j>=w[i])  
  38.                 m[i][j]=max(m[i-1][j],m[i-1][j-w[i]]+v[i]);  
  39.   
  40.             else  
  41.                 m[i][j]=m[i-1][j];  
  42.         }  
  43.     }/* 
  44.     for(int i=1;i<=6;i++) 
  45.     { 
  46.         for(int j=1;j<=c;j++) 
  47.         { 
  48.             cout<<m[i][j]<<' '; 
  49.         } 
  50.         cout<<endl; 
  51.     } 
  52. */  
  53.     traceback();  
  54.     for(int i=1;i<=n;i++)  
  55.         cout<<x[i];  
  56.     return 0;  
  57. }  

 

輸出x[i]數組:0111,輸出m[4][30]:22。

得出結論:選擇BCD三個項目總收益最大,爲22萬元。

 

不過這種算法只能獲得一種最優解,並不能得出全部的最優解。

相關文章
相關標籤/搜索