動態規劃求解0/1揹包問題

     

問題

給定n種物品和一個揹包,物品(1<=i<=n)重量是wI ,其價值vi, 揹包容量爲C,對每種物品只有兩種選擇:裝入揹包和不裝入揹包,即物品是不可能部分裝入,部分不裝入。如何選擇裝入揹包的物品,使其價值最大? ios

想法

該問題是最優化問題,求解此問題通常採用動態規劃(dynamic plan),很容易證實該問題知足最優性原理。 算法

動態規劃的求解過程分三部分: 函數

一:劃分子問題:將原問題劃分爲若干個子問題,每一個子問題對應一個決策階段,而且子問題之間具備重疊關係 性能

二:肯定動態規劃函數:根據子問題之間的重疊關係找到子問題知足遞推關係式(即動態規劃函數),這是動態規劃的關鍵 優化

三:填寫表格:設計表格,以自底向上的方式計算各個子問題的解並填表,實現動態規劃過程。 spa

     

思路:

如何定義子問題?0/1揹包能夠看作是決策一個序列(x1,x2,x3,…,xn,對任何一個變量xi的決策時xi=1仍是xi=0. V(n,C)是將n個物品裝入容量爲C的揹包時揹包所得到的的最大價值,顯然初始子問題是將前i個物品裝如容量爲0的揹包中和把0個物品裝入容量爲j的揹包中,這些狀況揹包價值爲0 設計

    V(i,0)=V(0,j)=0 0<=i<=n, 0<=j<=C blog

     

接下來考慮原問題的一部分,設V(I,j)表示將前i個物品裝入容量爲j的揹包得到的最大價值,在決策xi時,已經肯定了(x1,x2,…,xi-1),則問題處於下列兩種狀況之一: ci

  1. 揹包容量不足以裝入物品i,則裝入前i-1個物品的最大價值和裝入前i個物品最大價值相同,即xi=0,揹包價值沒有增長
  2. 揹包容量足以裝入物品i 若是把物品i裝入揹包,則揹包物品價值等於把前i-1個物品裝入容量爲j-wi的揹包中的價值加上第i個物品的價值vi;若是第i個物品沒有裝入揹包,則揹包價值等於把前i-1個物品裝入容量爲j的揹包中所取得的價值,顯然,取兩者最大價值做爲把物品i裝入容量爲j的揹包中的最優解,獲得以下遞推公式

         

爲了肯定裝入揹包中的具體物品,從V(n,C)的值向前推,若是VnC>V(n-1,C), it

則代表第n個物品被裝入揹包中,前n-1個物品被裝入容量爲C-wn的揹包中;不然,第n個物品沒有被裝入揹包中,前n-1個物品被裝入容量爲C的揹包中,依次類推,直到確認第一個物品是否被裝入揹包中

     

代碼C++實現

  1. // dp_01Knapsack.cpp : 定義控制檯應用程序的入口點。
  2. #include<iostream>
  3. #include<algorithm>
  4. using namespace std;
  5. const int N = 5;
  6. const int Capacity = 20;
  7. int flag[N+1] = { 0 };// 物品是否在揹包中,下標從 1開始算
  8. int V[N+ 1][Capacity + 1] = { 0 }; // 造表記錄子問題的最優解
  9. int Knapsack(int w[], int v[], int n, int C);// 實際物品數目,揹包容量
  10. int main()
  11. {
  12.    int w[] = {0,3,2,1,4,5};
  13.    int v[] = { 0,25,20,15,40,50 };
  14.    int n = 5, C = 6;
  15.    int maxValue = Knapsack(w, v, n, C);
  16.    cout << maxValue;
  17.     return 0;
  18. }
  19. int Knapsack(int w[], int v[], int n, int C) {
  20.    for (int i = 0; i <= n; ++i)
  21.       V[i][0] = 0;
  22.    for (int j = 0; j <= C; ++j)
  23.       V[0][j] = 0;
  24.    for(int i=1;i<=n;++i)
  25.       for (int j = 1; j <= C; ++j)
  26.       {
  27.          if (j < w[i])
  28.             V[i][j] = V[i - 1][j];
  29.          else {
  30.             V[i][j] = max(V[i - 1][j], V[i - 1][j - w[i]] + v[i]);
  31.          }
  32.       }
  33.      
  34.    for (int i = n, j = C; i > 0; --i)
  35.    {
  36.       if (V[i][j] > V[i - 1][j])
  37.       {
  38.          flag[i] = 1;
  39.          j -= w[i];
  40.       }
  41.       else
  42.          flag[i] = 0;
  43.    }
  44.    cout << " 造表\n ";
  45.    for (int i = 0; i <= n; ++ i)
  46.    {
  47.       for (int j = 0; j <= C; ++j)
  48.          cout << V[i][j] << '\t';
  49.       cout << endl;
  50.    }
  51.      
  52.    for (int i = 1; i <= n; ++i)
  53.       cout << flag[i] << '\t';
  54.    cout << endl;
  55.    return V[n][C];
  56. }

     

結果以下:

     

表格分析以下

   

   

0

1

2

3

4

5

6

flag

   

0

0

0

0

0

0

0

0

   

W1=3,v1=25

1

0

0

0

25

25

25

25

0

W2=2,v2=20

2

0

0

20

25

25

45

45

0

W3=1,v3=15

3

0

15

20

35

40

45

45

1

W4=4,v4=40

4

0

15

20

35

40

55

60

0

W5=5,v5=50

5

0

15

20

35

40

55

65

1

   

   

算法性能分析:

在算法Knapsack中,第一個for循環的時間性能是O(n),第二個for循環的時間性能是O(C),第三個循環是兩層嵌套for循環,時間性能是O(n*C),第四個for循環時間性能是O(n);

所以算法時間複雜度O(n*C)

相關文章
相關標籤/搜索