有 n 件物品和一個最大承重爲 W 的揹包,每件物品的重量是 𝑤i、價值是 𝑣ijava
在保證總重量不超過 W 的前提下,選擇某些物品裝入揹包,揹包的最大總價值是多少?數組
注意:每一個物品只有 1 件,也就是每一個物品只能選擇 0 件或者 1 件優化
這是一個典型的動態規劃問題:code
1.設置一個values的物品價值數組和一個相對應的物品重量數組weigths.blog
2.物品的編號爲i,因爲後續使用數組下標的緣由,i從1開始,因此:價值是values[i-1],重量爲weights[i-1]。ci
3.狀態方程:it
dp[i][j] = 表示有前i個物品能夠選擇,且揹包容量爲j的狀況下能夠獲取的最大價值
4.當 j < weights[ i -1 ]時:io
dp[i][j] = dp[i-1][j];
5.當 j > = weights[i-1] 時:class
dp [i][j] = Math.max (dp[i – 1, j], dp[i – 1, j – weights[i – 1]] + values[i – 1] ) values[i – 1] //表示當前物品的價值 dp[i – 1, j – weights[i – 1]] // 表示取了當前物品後剩餘空間的最大價值 //綜合:當j > = weights[i-1] 時,當前的物品能夠選擇取或者不取 (1)不取的話,很明顯,最大價值就是上一步的最大價值dp[i][j] = dp[i-1][j] (2)取的話,價值就爲dp[i – 1, j – weights[i – 1]] + values[i – 1] //因爲不知道取仍是不取時得到的價值最高,因此對兩個數取最大值。
class Solution{ public void knapsackProblem(){ int[] weight = {2,2,6,5,4}; int[] values = {6,3,5,4,6}; int capacity = 10; int n = values.length; //由於要空出第一行和第一列 因此dp數組要加一 int[][] dp = new int[values.length+1][capacity+1]; for (int i = 1; i < dp.length; i++) { for (int j = 1; j < dp[0].length; j++) { if (j < weight[i-1]){ dp[i][j] = dp[i-1][j]; }else { dp[i][j] = Math.max(dp[i-1][j],values[i-1]+dp[i-1][j-weight[i-1]]); } } } //輸出二維數組 for (int i = 0; i < dp.length; i++) { for (int j = 0; j < dp[0].length; j++) { System.out.print(dp[i][j] + " "); } System.out.println(); } //最終結果 System.out.println(dp[values.length][capacity]); } }
好比當咱們求dp[4][7]的時候,只有兩種選擇,要麼去4號物品,要麼不取。若是不取的話,那這一決策的最大價值就是上一步決策獲得的最大價值dp[3][7],若是取的話,就是當前物品的價值加上剩餘空間的最大價值dp[ 3 ][ 2 ] = 6, 6+4 = 10.和圖中結果同樣。遍歷
由上一個方案的執行過程咱們能夠發現這樣的規律:
當咱們求dp[i][j]時,只會用到其當前物品的價值和上一步決策的最大值(2號區域)以及前面的某一個數值(3號區域),那麼咱們就能夠把它簡化成一維數組,從後往前求取,這樣可使得代碼更加簡單。
package DynamicProgramming; public class KnapsackBest { public static void main(String[] args){ int[] w = {1,3,5,4,3}; int[] v = {5,7,10,8,6}; int c = 10; System.out.println(maxValue(w,v,c)); } public static int maxValue(int[] w,int[] v,int c){ //判斷數組是否符合要求 if (w.length == 0 || w == null) return 0; if (v.length == 0 || v == null) return 0; if (w.length != v.length || c <= 0) return 0; //用於存儲決策價值的數組 int[] dp = new int[c+1]; for (int i = 1; i < v.length+1 ; i++) { //從後往前遍歷,當j < w[i-1]的狀況下,直接使用上一步的數組結果 for (int j = c; j >= w[i-1] ; j--) { //從後往前,覆蓋上一物品時產生的結果 dp[j] = Math.max(dp[j],v[i-1] + dp[j-w[i-1]]); } } return dp[c]; } }