一、问题描述
给定n种商品,其中商品i的重量为wi,价值为vi。把这n种商品放入一个容量为c的背包,每种商品可以选择放入,也可以选择不放入。如何选择放入背包的商品,使背包中商品价值最大。
二、问题分析
考虑该问题的子问题,把该问题的解(x1,x2,......,xn)考虑为一个决策序列,依次决定xi是取1还是取0,其中1表示放入背包,0表示不放入背包。
记v(i,j)表示将前i个商品放入容量为j的背包,动态规划函数为
v(i,j) = v(i - 1,j) , j < wi ,背包容量不足装入物品i
= max(v(i - 1,j), v(i - 1,j - wi) + vi) j > wi,背包容量可以装入物品i,此时分两种情况:(1)不装入物品i,则可以把前i - 1个商品装入容量为j的背包中;(2)装入物品i,则只能把前i - 1个商品装入容量为j - wi的背包中。比较这两种情况下背包中商品最大价值即可。
边界值v(i,0) = 0,表示把前i个商品放入容量为0的背包中。
v(0,j) = 0,表示把前0个商品放入容量为j的背包中。
三、算法代码
public static void knapsack(int [] w, int [] v, int c){
int n = w.length;
int [][] value = new int[n + 1][c + 1];
int [] selected = new int[n];
for(int i = 0; i <= c; i++){//初始化第一行
value[0][i] = 0;
}
for(int i = 0; i <= n; i++){//初始化第一列
value[i][0] = 0;
}
for(int i = 0; i <= n - 1; i++){//填表,第0个物品对应第1行,以此类推
for(int j = 1; j <= c; j++){
if(w[i] > j){
value[i + 1][j] = value[i][j];
}else{
value[i + 1][j] = Math.max(value[i][j], value[i][j - w[i]] + v[i]);
}
}
}
//寻找哪些物品放入背包
for(int i = n, j = c; i > 0;i--){
if(value[i][j] > value[i - 1][j]){
selected[i - 1] = 1;
j = j - w[i - 1];
}else{
selected[i - 1] = 0;
}
}
System.out.print("物品选择情况为(1代表放入):");
for(int i = 0; i <= n - 1; i++){
System.out.print(selected[i] + " ");
}
System.out.println();
//打印出背包内物品最大价值
System.out.println("放入物品后背包总价值为:" + value[n][c]);
}
四、完整测试代码
public class package01 {
public static void main(String[] args) {
int [] w = new int[]{2,2,6,5,4};
int [] v = new int[]{6,3,5,4,6};
int c = 10;
knapsack(w, v, c);
}
public static void knapsack(int [] w, int [] v, int c){
int n = w.length;
int [][] value = new int[n + 1][c + 1];
int [] selected = new int[n];
for(int i = 0; i <= c; i++){//初始化第一行
value[0][i] = 0;
}
for(int i = 0; i <= n; i++){//初始化第一列
value[i][0] = 0;
}
for(int i = 0; i <= n - 1; i++){//填表,第0个物品对应第1行,以此类推
for(int j = 1; j <= c; j++){
if(w[i] > j){
value[i + 1][j] = value[i][j];
}else{
value[i + 1][j] = Math.max(value[i][j], value[i][j - w[i]] + v[i]);
}
}
}
//寻找哪些物品放入背包
for(int i = n, j = c; i > 0;i--){
if(value[i][j] > value[i - 1][j]){
selected[i - 1] = 1;
j = j - w[i - 1];
}else{
selected[i - 1] = 0;
}
}
System.out.print("物品选择情况为(1代表放入):");
for(int i = 0; i <= n - 1; i++){
System.out.print(selected[i] + " ");
}
System.out.println();
//打印出背包内物品最大价值
System.out.println("放入物品后背包总价值为:" + value[n][c]);
}
}
五、运行结果
物品选择情况为(1代表放入):1 1 0 0 1
放入物品后背包总价值为:15