動態規劃 01揹包問題

NewCoder1024發表於2020-03-08
題目來源:牛客網 01揹包


現有一個容量大小為V的揹包和N件物品,每件物品有兩個屬性,體積和價值,請問這個揹包最多能裝價值為多少的物品

輸入描述:

第一行兩個整數V和n 接下來n行,每行兩個整數體積和價值,1≤N≤1000,1≤V≤20000 每件物品的體積和價值範圍在[1,500]

輸出描述:

輸出揹包最多能裝的物品價值

Example:

Input:  6 3
        3 5
        2 4
        4 2
Output: 9
複製程式碼

原理

根據動態規劃解題步驟(問題抽象化、建立模型、尋找約束條件、判斷是否滿足最優性原理、找大問題與小問題的遞推關係式、填表、尋找解組成)找出01揹包問題的最優解以及解組成,然後編寫程式碼實現。

動態規劃與分治法類似,都是把大問題拆分成小問題,通過尋找大問題與小問題的遞推關係,解決一個個小問題,最終達到解決原問題的效果。但不同的是,分治法在子問題和子子問題等上被重複計算了很多次,而動態規劃則具有記憶性,通過填寫表把所有已經解決的子問題答案紀錄下來,在新問題裡需要用到的子問題可以直接提取,避免了重複計算,從而節約了時間,所以在問題滿足最優性原理之後,用動態規劃解決問題的核心就在於填表,表填寫完畢,最優解也就找到。

最優性原理是動態規劃的基礎,最優性原理是指“多階段決策過程的最優決策序列具有這樣的性質:不論初始狀態和初始決策如何,對於前面決策所造成的某一狀態而言,其後各階段的決策序列必須構成最優策略”。

實現細節(重要)

核心資料結構是dp[i][j],指在揹包當前容量為j的情況下,前i個物品最佳組合的價值。

  • 01揹包的資料從dp[0][0]開始增長,顯然dp[0][0]是值為0的情況。
  • 此後以先討論前i個物品在不同當前剩餘揹包容量j的情況下的最大可容納值。
  • w[i]表示當前第i件物品的體積:
  1. 如果dp[i][j]中的j小於w[i],說明在當前要裝的第j個物品不能被放入dp[i-1][j]表示的情況,即j<w[i] dp[i][j] = dp[i-1][j]。在這種情況下沒得選,只能將第i件物品放棄,即保持dp[i-1][j]表示的情況。
  2. 如果dp[i][j]中的j大於w[i],說明在當前要裝的第j個物品可以被放入dp[i-1][j]表示的情況,這個時候有兩種選擇:放、不放。那選擇標準肯定是最大化的價值,即j>=w[i] dp[i,j] = max {dp[i-1,j],dp[i-1,j-w[i]]+v[i]},其比較過程在max函式中完成。關鍵是理解不放的時候比放的時候價值還要大的情況,即同樣在前i-1的種類中,可能dp[i-1,j-w[i]]會比dp[i-1,j]小很多的情況(當前可用揹包容量j-w[i]太小,即使加上了v[i]也不足以超過同樣種類容量為j時的最佳方案)。

表格填完最優解即是右下角的值。

程式碼

#include<iostream>
#include<algorithm>
using namespace std;

int main(){
    int V = 0, n = 0;
    //讀入揹包容量和物品數量
    cin>>V>>n;
    int w[n+1],v[n+1],dp[n+1][V+1];

    //初始化矩陣值
    for(int i = 0;i<n+1;i++){
        for(int j = 0;j<V+1;j++){
            dp[i][j] = 0;
        }
    }

    //依次讀入物品體積及價值
    for(int i = 1; i<=n; i++){
        cin>>w[i]>>v[i];
    }

    //按前i件物品的順序填表
    for(int i = 1;i<=n;i++){
        //噹噹前剩餘容量j小於揹包的最大值時
        for(int j = 1;j<=V;j++){
            //程式碼實現兩種情況
            if(j<w[i]){
                dp[i][j] = dp[i-1][j];
            }else{
                dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]] + v[i]);
            }
        }
    }

    //輸出右下角的值
    cout<<dp[n][V]<<endl;
    return 0;
}複製程式碼

揹包問題最優解回溯

通過上面的方法可以求出揹包問題的最優解,但還不知道這個最優解由哪些商品組成,故要根據最優解回溯找出解的組成,根據填表的原理可以有如下的兩種尋解方式

  1. dp[i][j] = dp[i-1][j]時,即在i-1種物品的最優情況下,不加入(可能是由於空間不夠或者是實現細節所述的非最優解情況)第i種物品是前i種物品的最優情況,即回到dp[i-1,j]
  2. dp[i,j] = dp[i-1,j-w[i]]+v[i]時,即在i-1種物品的最優情況下,加入了第i種物品,該物品是最優解組成的一部分,隨後我們得回到裝該物品之前,即回到dp[i-1,j-w[i]]

根據上述方法遍歷表格即可獲得最優解的記錄。

相關文章