【動態規劃】0-1揹包問題

一、問題

有N件物品和一個容量(最大承重)爲V的揹包。第i件物品的體積(重量)是w[i],價值是p[i]。求解將哪些物品裝入揹包可使價值總和最大。
所謂01揹包,表示每一個物品只有一個,要麼裝入,要麼不裝入。

二、分析

0-1揹包屬於求最優解,當然用動態規劃解決,當然也可以用其他方法解決。這裏介紹動態規劃方法。
階段是:在前N件物品中,選取若干件物品放入揹包中;
狀態是:在前N件物品中,選取若干件物品放入所剩空間爲W的揹包中的所能獲得的最大價值;
決策是:第N件物品放或者不放;
dp[i][j]是:在前 i 件物品中選擇若干件放在所剩空間爲 j 的揹包裏所能獲得的最大價值;
狀態轉移方程
1. dp[i][j] = 0 where j=0 or i =0
2. dp[i][j] = dp[i-1][j] where j< w[i]
3. dp[i][j] = max{dp[i-1][j], dp[i-1][j-w[i]] + p[i]} where j >= w[i]
解釋:第一種是第i件不放進去,這時所得價值爲:f[i-1][v]
第二種是第i件放進去,這時所得價值爲:f[i-1][v-c[i]]+w[i]

三、實現

void display(int N,int V, int w[],int p[],int *dp)
{
#define dp(i,j) *(dp + (i)*(V+1) + (j))
    int i=N,j=V;
    vector<int> vrst;
    while(i>0 && j>0)
    {
        if (dp(i,j) != dp(i-1,j))
        {
            vrst.push_back(i);
            j = j - w[i-1];
        }
        i--;
    }
    for(vector<int>::iterator iter = vrst.begin();
    iter != vrst.end();
    ++iter)
    {
        cout << *iter << " " ;
    }
#undef dp
}
//n個物品,容量爲V,w爲體積,p爲價值
void pack_of_01(int N,int V,int w[],int p[])
{
    int i,j;
    int* dp = (int*)malloc(sizeof(int)*(N+1)*(V+1));
#define dp(i,j) *(dp + (i)*(V+1) + (j))
    memset(dp,0,sizeof(int)*(V+1)*(N+1));
    for (i=1;i<=N;i++)
    {
        for(j=1;j<=V;j++)
        {
            dp(i,j) = dp(i-1,j);
            //注意此時的i對應w和p中的i-1,因爲dp多加了一個0行0列
            if (j >= w[i-1] && (dp(i-1,j-w[i-1]) + p[i-1]) > dp(i-1,j))
            {
                dp(i,j) = dp(i-1,j-w[i-1]) + p[i-1];
            }
        }
    }
    for (i=1;i<=N;i++)
    {
        for(j=1;j<=V;j++)
        {
            cout << dp(i,j) << " ";
        }
        cout << endl;
    }
    display(N,V,w,p,dp);
    free(dp);
#undef dp
}

測試代碼:

    int p[T] = {12 , 10 , 20, 15 };      
    int w[T] = {2 , 1 , 3 , 2 }; 
    pack_of_01(4,5,w,p);

結果:
這裏寫圖片描述

四、優化

自己想了一種優化方法,就是隻要O(V)的空間,因爲dp[i][j]總是找dp[i-1][j]或者dp[i-1][j-w[i]]。所以每次從後往前遍歷,相當於二維數組從後往前,從上往下遍歷。但是,這隻能獲得最優解,不能獲得最優的那個幾個揹包。
代碼如下:

int packof01()
{
    for(int i = 0 ; i <= V ;i++) //條件編譯,表示揹包可以不存儲滿
        f[i] = 0 ;    
    for(int i = 0 ; i < N ; i++)
    {
        for(int v = V ; v >= w[i] ;v--) //必須全部從V遞減到0
        {              
            f[v] = max(f[v-w[i]] + p[i] , f[v])  ; //此f[v]實質上是表示的是i-1次之前的值。
        }                 
    }
    return f[V] ;        
}

五、擴展

01揹包(ZeroOnePack):

有N件物品和一個容量爲V的揹包。(每種物品均只有一件)第i件物品的費用是c[i],價值是w[i]。求解將哪些物品裝入揹包可使價值總和最大。

完全揹包(CompletePack):

有N種物品和一個容量爲V的揹包,每種物品都有無限件可用。第i種物品的費用是c[i],價值是w[i]。求解將哪些物品裝入揹包可使這些物品的費用總和不超過揹包容量,且價值總和最大。

多重揹包(MultiplePack):

有N種物品和一個容量爲V的揹包。第i種物品最多有n[i]件可用,每件費用是c[i],價值是w[i]。求解將哪些物品裝入揹包可使這些物品的費用總和不超過揹包容量,且價值總和最大。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章