揹包問題:n種物品,每種物品有重量w和價值v,揹包所能承受的最大重量爲c。如何挑選物品可使總物品的價值最大。java
0-1揹包問題:限定每種物品的個數只能是0或1.算法
如:5個物品,質量分別爲3,5,7,8,9,價值分別爲4,6,7,9,10。揹包所能承受重量爲22.ide
給物品從0開始編號,挑選物品1,3,4(價值分別爲6,9,10)時,總價值最大爲25,此時重量爲22.spa
運用動態規劃思想,能夠構造有效的方法。code
算法基本思路:blog
1.構造最優表,獲取最大價值;字符串
構造表mv[0...n][0...c],mv[i][j] 表示 前 i 項物品(即0到i-1)中挑選物品放入承受重量爲 j 的最大價值。get
顯然,當 i 爲0即沒有物品時,mv[0][*] = 0;當 j 爲0即最大重量爲0時,mv[*][0] = 0。(*表示任意)input
對於mv[i][j]只有兩種可能的狀況,it
(要注意的是,i 表示前i 項物品,物品編號從0開始,即 i-1 表示第i 項物品)
(1)當w[i-1] > j 即當前物品重量大於當前最大承受重量時,只能放棄當前物品即 mv[i][j] = mv[i-1][j];
(2)當w[i-1] <= j 即當前物品重量小於當前最大承受重量時,經過比較取此物品後的最大價值(即mv[i-1][j-w[i-1]] + v[i-1])與不取此物品的最大價值(即mv[i-1][j]),挑出較大值即 mv[i][j] = max(mv[i-1][j], mv[i-1][j-w[i-1]])。
經過上述自底向上打表,可構造出最優值mv[n][c]。
2.經過最優表,獲取構造路線即挑出能夠獲得最大價值的物品。
經過上述可知,當w[i-1] > j 時,mv[i][j] = mv[i-1][j];當w[i-1] <= j 時,mv[i][j] = max(mv[i-1][j], mv[i-1][j - w[i-1]] + v[i-1])。
逆推此過程,從mv[n][c]出發,一直到 i 爲 0(沒有物品)或 j 爲 0(沒有更多重量)。
當w[i-1] > j 時,可知沒有挑選此物品,此時 i - 1;
當w[i-1] <= j 時,再比較 mv[i][j] 是否與 mv[i-1][j] 相等,如果,則沒有挑選此物品,此時 i - 1;若不等,則說明有挑選此物品,此時 i - 1, j - w[i-1]。
算法實現:
基本上和上述相似,但須要注意的是,返回結果,我同時返回了最優值和物品編號,涉及一些java處理字符串的小技巧,和此算法無關,能夠忽略。
/* input: w[0..n-1] //w[i] = the weight of Object i v[0..n-1] //v[i] = the value of Object i c //c = the weight of Knapsack table: mv[0..n][0..C] //mv[i][j] = select some objects from O[0..i-1], then satify the max value(weight < weight j) if i = 0, means no objects, so mv[0][*] = 0 if j = 0, means no weights, so mv[*][0] = 0 else if w[i-1] > j, means not enough weight, so mv[i][j] = mv[i-1][j] if w[i-1] <= j, means enough weight, so mv[i][j] = min(mv[i-1][j], mv[i-1][j-w[i-1]] + v[i-1]) //note that mv[i-1][j-w[i]] means the best answer of O[0..i-1] in weight j-w[i-1] //note that i means ith object, so it is w[i-1] rather than w[i] */ import java.util.ArrayList; public class Knapsack { public static String getMaxValue(int[] w, int[] v, int c){ int[][] mv = new int[w.length+1][c+1]; //i = 0 for(int j = 0; j <= c; j ++){ mv[0][j] = 0; } //j = 0 for(int i = 0; i <= w.length; i ++){ mv[i][0] = 0; } //compute for(int i = 1; i < mv.length; i ++){ for(int j = 1; j <= c; j ++){ if(w[i-1] > j){ mv[i][j] = mv[i-1][j]; } else{ mv[i][j] = Math.max(mv[i-1][j], mv[i-1][j-w[i-1]] + v[i-1]); } } } // for(int i = 0; i < mv.length; i ++){ // for(int j = 0; j < mv[0].length ; j ++){ // System.out.print(mv[i][j] + " "); // } // System.out.println(); // } //get result ArrayList<Integer> index = getObjectsIndex(w, v, c, mv); String result = Integer.toString(mv[w.length][c]); for(int i = 0; i < index.size(); i ++) result += "/" + Integer.toString(index.get(i)); return result; } private static ArrayList<Integer> getObjectsIndex(int[] w, int[] v, int c, int[][] mv){ ArrayList<Integer> index = new ArrayList<Integer>(); int i = mv.length-1, j = c; while(i > 0 && j > 0){ if(w[i-1] > j){ i --; } else{ if(mv[i-1][j] == mv[i][j]){ i --; } else{ index.add(i-1); j -= w[i-1]; i --; } } } return index; } public static void main(String[] args) { int c = 22; int[] w = {3, 5, 7, 8, 9}; int[] v = {4, 6, 7, 9, 10}; String[] result = getMaxValue(w, v, c).split("/"); System.out.println(result[0]); for(int i = 1; i < result.length; i ++) System.out.print(result[i] + " "); System.out.println(); } }