0-1背包问题
背包问题:n种物品,每种物品有重量w和价值v,背包所能承受的最大重量为c。如何挑选物品可以使总物品的价值最大。
0-1背包问题:限定每种物品的个数只能是0或1.
如:5个物品,质量分别为3,5,7,8,9,价值分别为4,6,7,9,10。背包所能承受重量为22.
给物品从0开始编号,挑选物品1,3,4(价值分别为6,9,10)时,总价值最大为25,此时重量为22.
运用动态规划思想,可以构造有效的方法。
算法基本思路:
1.构造最优表,获取最大价值;
构造表mv[0...n][0...c],mv[i][j] 表示 前 i 项物品(即0到i-1)中挑选物品放入承受重量为 j 的最大价值。
显然,当 i 为0即没有物品时,mv[0][*] = 0;当 j 为0即最大重量为0时,mv[*][0] = 0。(*表示任意)
对于mv[i][j]只有两种可能的情况,
(要注意的是,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(); } }