天天看點

動态規劃——0/1背包問題

動态規劃是用空間換時間的一種方法的抽象。其關鍵是發現子問題和記錄其結果。然後利用這些結果減輕運算量。

比如01背包問題。

因為背包最大重量M未知。是以,我們的程式要從1到M一個一個的試。比如,開始任選N件物品的一個。看對應M的背包,能不能放進去,如果能放進去,并且還有多的空間,則,多出來的空間裡能放N-1物品中的最大價值。怎麼能保證總選擇是最大價值呢?看下表。

測試資料:

10,3

3,4

4,5

5,6

動态規劃——0/1背包問題

c[i][j]數組儲存了1,2,3号物品依次選擇後的最大價值.

這個最大價值是怎麼得來的呢?從背包重量為0開始,1号物品先試,0,1,2,的重量都不能放.是以置0,背包重量為3則裡面放4.這樣,這一排背包重量為4,5,6,....10的時候,最佳方案都是放4.假如1号物品放入背包.則再看2号物品.當背包重量為3的時候,最佳方案還是上一排的最價方案c為4.而背包重量為5的時候,則最佳方案為自己的重量5.背包重量為7的時候,很顯然是5加上一個值了。加誰??很顯然是7-4=3的時候.上一排 c3的最佳方案是4.是以。總的最佳方案是5+4為9.這樣.一排一排推下去。最右下放的資料就是最大的價值了。(注意第3排的背包重量為7的時候,最佳方案不是本身的6.而是上一排的9.說明這時候3号物品沒有被選.選的是1,2号物品.是以得9.)

從以上最大價值的構造過程中可以看出。

f(n,m)=max{f(n-1,m), f(n-1,m-w[n])+P(n,m)}這就是書本上寫的動态規劃方程.這回清楚了嗎?

按照《計算機算法設計與分析》中所講到的,按照動态規劃的解題方法,步驟如下:

1,分析問題

 由于對于一個物件序列a1~an,每個的選擇的可能隻能是0或1,于是問題就轉化為尋找一個0/1整數序列x1~xn,xi=0或xi=1。

2,尋找最優子結構。

假設,x1~xi是在物件序列a1~ai中選擇的一個最優選擇序列,那麼,x1~x(i-1)一定是從物件序列a1~a(i-1)選擇的最有選擇序列。

但是,需要注意的是,物件序列a1~a(i-1)和a1~ai在進行選擇時,它們的限制條件不一樣,即要求的最大重量限制不同。是以不能簡單的按照一般的子問題來考慮(即,設c[i]為從a1~ai中最優選擇的最大價值,那麼c[i]=c[i-1]+vi,這樣沒有考慮到最大重量限制)。

當考慮到最大重量限制時,設c[i][j]為“從物件序列a1~ai中選擇一個整數序列x1~xi,且使總重量不超過j時的最大總價值”。

重新考慮最優子結構:x1~x(i-1)為從物件序列a1~a(i-1)中選擇的最優整數序列,并且保證:

(1)當若xi=1,即選擇了物件ai,則需保證總重量不超過j-wi。

(2)當若xi=0,即不選擇ai,則需保證總重量不超過j。

于是,c[i][j] =

(j>=ai時)                  

                                max(c[i-1][j]),  //不選擇ai

                                c[i-1][j-wi]+vi) //選擇ai

(j<ai時)

                                c[i-1][j]

下面是實際程式:

#include<stdio.h>

int c[10][100];

int knapsack(int m,int n)

{

 int i,j,w[10],p[10];

 for(i=1;i<n+1;i++)

        scanf("/n%d,%d",&w[i],&p[i]);

 for(i=0;i<10;i++)

      for(j=0;j<100;j++)

           c[i][j]=0;

 for(i=1;i<n+1;i++)

      for(j=1;j<m+1;j++)

           {

            if(w[i]<=j)

                     {

                      if(p[i]+c[i-1][j-w[i]]>c[i-1][j])

                            c[i][j]=p[i]+c[i-1][j-w[i]];

                            else

                            c[i][j]=c[i-1][j];

                     }

              else c[i][j]=c[i-1][j];

            }

 return(c[n][m]);

}

int main()

{

    int m,n;int i,j;

    scanf("%d,%d",&m,&n);

    printf("Input each one:/n");

    printf("%d",knapsack(m,n));

    printf("/n");

     for(i=0;i<10;i++)

      for(j=0;j<15;j++)

         {

          printf("%d ",c[i][j]);

             if(j==14)printf("/n");

         }

    system("pause");

}

http://blog.csdn.net/adcxf/archive/2008/08/07/2784156.aspx