揹包問題(Knapsack problem)是一種組合優化的NP徹底問題。問題能夠描述爲:給定一組物品,每種物品都有本身的重量和價格,在限定的總重量內,咱們如何選擇,才能使得物品的總價格最高。問題的名稱來源於如何選擇最合適的物品放置於給定揹包中。java
咱們有 n 種物品,物品 j 的重量爲wj,價格爲pj。
咱們假定全部物品的重量和價格都是非負的。揹包所能承受的最大重量爲W。算法
若是限定每種物品只能選擇0個或1個,則問題稱爲0-1揹包問題。數組
若是限定物品j最多隻能選擇bj個,則問題稱爲有界揹包問題。優化
若是不限定每種物品的數量,則問題稱爲無界揹包問題。code
動態規劃背後的基本思想很是簡單。大體上,若要解一個給定問題,咱們須要解其不一樣部分(即子問題),再根據子問題的解以得出原問題的解。table
一般許多子問題很是類似,爲此動態規劃法試圖僅僅解決每一個子問題一次,從而減小計算量:一旦某個給定子問題的解已經算出,則將其記憶化存儲,以便下次須要同一個子問題解之時直接查表。class
舉例,令物品數量N=5,揹包所能承受的最大重量爲W=10,物品與價格的對應關係以下表左三列所示。循環
name | weight | value | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|---|---|
a | 2 | 6 | 0 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 | 6 |
b | 2 | 3 | 0 | 6 | 6 | 9 | 9 | 9 | 9 | 9 | 9 | 9 |
c | 6 | 5 | 0 | 6 | 6 | 9 | 9 | 9 | 9 | 11 | 11 | 14 |
d | 5 | 4 | 0 | 6 | 6 | 9 | 9 | 9 | 10 | 11 | 13 | 14 |
e | 4 | 6 | 0 | 6 | 6 | 9 | 9 | 12 | 12 | 15 | 15 | 15 |
當放入物品a時,在揹包所能承受的重量內,計算揹包擁有的物品總價格,並進行標記,如表格第一行所示,當揹包所能承受的重量大於等於2時,均可以放入物品a,揹包擁有的物品總價格爲6。遍歷
接着咱們放入物品b,放入以前,一是要判斷揹包是否所能承受其重量,二是判斷放入以後與放入以前擁有的物品總價格哪一個最大,如表格第二行所示,當揹包所能承受的重量大於等於2時,均可以放入物品b,可是,物品b在揹包容量爲[2,3]的時候,放入以後的總價格3不如放入以前的總價格6大,因此不放入。static
當揹包所能承受的重量等於4時,放入物品b後,揹包所能承受的重量4減去物品b的重量2後,剩餘的所能承受的重量2還能夠放入物品a,此時揹包擁有的物品總價格爲物品a和物品b的總價格之和,即爲9,大於放入以前的物品總價格6,因此此時揹包擁有的物品總價格最大爲9。
分析可知,在一層循環遍歷下,咱們須要一個一維數組保存揹包所能承受的最大重量與其擁有的物品總價格,並不斷更新。
/** * 0-1揹包問題 * * @param N 物品數量 * @param W 揹包容量 * @param weight 物品重量 * @param value 物品價格 * @return 最大價值 */ private static int traceBack(int N, int W, int[] weight, int[] value) { int[] dp = new int[W + 1]; // 範圍[0,W] 當前揹包容量對應的物品總價值 for (int i = 0; i < N; i++) { // 一件、一件的向包中放入每件物品 for (int j = W; j >= weight[i]; j--) { //只要揹包能夠放入就放 dp[j] = Math.max(dp[j - weight[i]] + value[i], dp[j]); // 比較放入物品以前與放入以後的價值哪一個大 } } return dp[W]; }
顯而易見,算法須要的時間複雜度爲O(nW),空間的消耗(即所需的一維數組)爲O(W)。