給定 n 種物品和一個容量爲 C 的揹包,物品 i 的重量是 $w_i$,其價值爲 $v_i$
問:應該如何選擇裝入揹包的物品,使得裝入揹包中的物品的總價值最大?
揹包問題是具備許多應用的組合優化問題算法
在揹包問題中,咱們有一組物品。每一個物品都有重量和價值:編程
咱們想將這些物品放入揹包。可是,它有一個重量限制:數組
所以,咱們須要選擇總重量不超太重量限制的物品,而且其總價值達到最高。 例如,上述示例的最佳解決方案是選擇5kg和6kg物品,它們在重量限制內的最大值爲40元
。單元測試
揹包問題有幾種變化,咱們將重點介紹0-1揹包問題。在0-1揹包問題中,必須選擇每一個物品或將其留在後面。咱們不能取一部分物品。另外,咱們不能屢次取一件物品。測試
如今讓咱們以數學符號形式化0-1揹包問題。給定一組n個物品和重量限制W,咱們能夠將優化問題定義爲:優化
**這個問題是NP徹底難題
)。**所以,目前尚無多項式時間算法能夠解決。可是,對於此問題,可使用動態規劃算法思路來解決。spa
使用遞歸公式來解決此問題:3d
在該公式中,$M_(n,w)$ 是重量限制爲w的n個物品的最優解。它是如下兩個值中的最大值:code
重量限制爲w的(n-1)個物品的最優解(不包括第n個項目)
第n個物品的值加上(n-1)個物品的最優解和w減去第n個物品的權重(包括第n個物品)
若是第n項的重量大於當前的重量限制,則不包括在內。所以,它屬於上述兩種狀況的第一類。blog
Java中實現此遞歸公式代碼:
在每一個遞歸步驟中,咱們須要評估兩次最優解決邏輯,所以,此遞歸解決方案的運行時間爲O($2^n$)。
動態規劃思路是一種用於線性化,指數級遞增的編程算法的思路,這個思路是存儲子問題的結果,這樣咱們之後就沒必要從新計算它們了。
咱們還能夠經過動態規劃解決0-1揹包問題。要使用動態規劃中,咱們使用自下而上的方法來計算最佳解決方案:
/** * @param w : 已知物品重量數組集合 * @param v : 已知物品價值數組集合 * @param n : 最大價值,0 表示不要求 * @param W : 最大重量,0 表示不要求 * @author 油膩的Java * @date 2019/11/5 * @return */ public int knapsackDP(int[] w, int[] v, int n, int W) { if (n <= 0 || W <= 0) { return 0; } /** * 建立臨時數組 */ int[][] m = new int[n + 1][W + 1]; for (int j = 0; j <= W; j++) { m[0][j] = 0; } /** * 遍歷n和W, */ for (int i = 1; i <= n; i++) { for (int j = 1; j <= W; j++) { if (w[i - 1] > j) { //對m數組進行賦值 m[i][j] = m[i - 1][j]; } else { //求最大值 m[i][j] = Math.max(m[i - 1][j], m[i - 1][j - w[i - 1]] + v[i - 1]); } } } return m[n][W]; }
在代碼中,咱們在商品價值n和重量限制W上有一個嵌套循環。所以,它的運行時間爲O(nW)。
最後針對各自的場景作了一下單元測試
同時知足價格,重量最大化
最大重量最優解
最大價格最優解
本文經過編寫遞歸算法、動態規劃算法來解決揹包0-1問題,以及測試相應的單元測試,但願在揹包算法對你有新的認知。