背包問題解題方法總結


最近在牛客刷題遇到好幾道背包問題,索性這兩天集中火力刷了一些這類的題。這里總結一下0-1背包完全背包多重背包三種基本的背包問題的解題套路。(均基於動態規划的思想)

0-1背包

題目:有 N 件物品和容量為 W 的背包。第 i 件物品的重量為 w_i,價值為 v_i,求將不超過背包容量的物品裝入背包能得到的最大價值。

特點,每件物品的數量只有一個,可以選擇放或不放某件物品。

dp[i][j]表示將前 i+1 件總重量不超過 j 的物品放入背包能獲得的最大價值,則可以用以下的轉移方程來表示這個過程:

\[dp[i,j] = max(dp[i - 1, j], dp[i-1, j-w[i]] + v[i]) \]

注意到dp數組第i行的值更新只跟 i-1 行有關,因此可以通過滾動數組或者反向更新的方式優化一下空間復雜度,在動態規划解題的時候這是一種常用的空間復雜度優化方式。優化后的代碼如下:

for(int i = 0; i < N; i++){
    // 注意到這里dp需要從后往前更新,避免更新前就把舊值覆蓋
    // 從實際意義上來說,因為每件物品只有一個,從后向前更新保證了更新是在還沒放入過當前物品的前提下進行的
    for(int j = W; j >= w[i]; j--){  
        dp[j] = Math.max(dp[j], dp[j - w[i]] + v[i]);
    }
}

完全背包

題目:有 N 種物品和容量為 W 的背包。第 i 種物品的重量為 w_i,價值為 v_i,每種物品的數量無限。求將不超過背包容量的物品裝入背包能得到的最大價值。

特點:每種物品的數量無限多。

考慮到每種物品的數量無限。用 dp[j] 表示在重量不超過 j 的情況下背包中物品可以達到的最大價值,則轉移方程如下:

\[dp[j]=max(dp[j], dp[j-w[i]]+v[i]) \]

核心代碼如下:

for(int i = 0; i < N; i++){
    for(int j = w[i]; j <= W; j++){    // 這里和0-1背包不同
        dp[j] = Math.max(dp[j], dp[j - w[i]] + v[i]);
    }
}

注意內層for循環是從前向后更新dp數組的,這是唯一和上面的0-1背包問題區別的地方。原因在於,題目中每種物品的數量無限多,在放入一件物品 i 時,要考慮之前已經放過物品 i 的情況。

多重背包

題目:有 N 種物品和容量為 W 的背包。第 i 種物品的數量有n_i個,每個物品重量為 w_i,價值為 v_i,每種物品的數量無限。求將不超過背包容量的物品裝入背包能得到的最大價值。

特點:每種物品的數量不止一個,但有限。

基本的多重背包問題狀態轉移方程如下:

\[dp[j]=max(dp[j],dp[j-k*w[i]]+k*v[i]), 0\leq k \leq n[i] \]

核心代碼如下:

for(int i = 0; i < N; i++){
    for(int j = W; j >= w[i]; j--){  
        for(int k = 1; k <= n[i]; k++){
            if(j < k * w[i])  break; 
            dp[j] = Math.max(dp[j], dp[j - w[i] * k] + v[i] * k);
        }
    }
}

一些背包問題的題目

上面討論的三種情況只是最基本的背包問題,實際刷題過程中會遇到這些基本問題的變體,例如需要背包正好裝滿、求最小的物品件數、求裝包的方案數等等。這里整理一些題目,后面遇到了持續更新~

0-1背包類題目

考試策略
籃球隊 - 求方案數
牛妹的春游 - 最小花費,多個條件,初始值限制
服務部署 - 多重條件

完全背包類題目

換零錢 - 求總方案數
最少數量貨物裝箱問題 - 物品件數最小,恰好裝滿
拼湊面額 - 求方案數

多重背包類題目

喬喬的包


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM