问题描述:
一个容量为V的背包,现在有N件商品(相同的商品可以随意取为完全背包,最多只能取一次为01背包),每件商品的重量是w1,价值是v1,请问,这个背包所能容纳的最大价值是多少?
package algorithm;
public class KnapsackProblem {
public static void main(String[] args) {
int[] value = {1000, 3000, 5000, 3500};
int[] weight = {1, 2, 4, 3};
int capacity = 10;
solution(capacity, weight, value);
solution2(capacity, weight, value);
}
//01背包, 即每个物品最多被放入一次
private static void solution(int capacity, int[] weight, int[] value) {
//建立一个二维数组,列值为背包的容量,行值代表当前可选择的商品
int[][] res = new int[weight.length + 1][capacity + 1];
//res数组的第一行表示,没有商品可以选择,背包的价值应该为默认值0
//res数组的第一列表示,背包的容量为0,所以背包的价值也为0
for(int i = 1; i <= weight.length; i++ ){
for (int j = 1; j <= capacity ; j++) {
if(weight[i-1] > j){ //表示将要加入的物品重量大于当前背包的容量,即不能加入该商品
res[i][j] = res[i-1][j];
}else{
//尝试将该商品加入到背包,这个时候背包中的总价值应该是该商品的价值加上背包剩余容量能装入的最大价值。
int tryValue = value[i - 1] + res[i-1][j-weight[i - 1]];
//将放入和不放入两种情况进行比较找到更优的解决方案
if(tryValue > res[i-1][j]){
res[i][j] = tryValue;
}else{
res[i][j] = res[i-1][j];
}
}
}
}
//打印res数组
for (int i = 0; i< res.length; i++){
for (int j = 0; j < res[0].length; j++) {
System.out.print(res[i][j] + " ");
}
System.out.println();
}
//打印装入背包的策略,从大到小反向打印
for (int i = res.length -1, j = res[0].length-1; i >0 && j >0 ; i--) {
if(res[i][j] != res[i-1][j]){//说明第i件商品被选择,因为每件物品只能放置最多一件,直接减少物品种类,即i--
System.out.printf("Put in %d good \n", i);
j = j - weight[i-1];
}
}
}
//完全背包问题,即每个物品不限制放入的次数
private static void solution2(int capacity, int[] weight, int[] value) {
//建立一个二维数组,列值为背包的容量,行值代表当前可选择的商品
int[][] res = new int[weight.length + 1][capacity + 1];
//res数组的第一行表示,没有商品可以选择,背包的价值应该为默认值0
//res数组的第一列表示,背包的容量为0,所以背包的价值也为0
for(int i = 1; i <= weight.length; i++ ){
for (int j = 1; j <= capacity ; j++) {
//定义k, 表示尝试将第i个物品放入背包的次数
for (int k = 0; k * weight[i - 1] <= j; k++) {
//尝试将该商品加入到背包k个时,背包的价值
int tryValue = value[i - 1] * k + res[i-1][j-weight[i - 1] * k];//当k=0时,相当于初始化res[i][j]的值等于res[i-1][j]
if (tryValue > res[i][j]){
res[i][j] = tryValue;
}
}
}
}
//打印res数组
for (int i = 0; i< res.length; i++){
for (int j = 0; j < res[0].length; j++) {
System.out.print(res[i][j] + " ");
}
System.out.println();
}
//打印装入背包的策略,从大到小反向打印
for (int i = res.length -1, j = res[0].length-1; i >0 && j >0 ; ) {
if(res[i][j] != res[i-1][j]){//说明第i件商品被选择;将当前背包容量减少,继续比较是否放入该商品
System.out.printf("Put in %d good \n", i);
j = j - weight[i-1];
}else {
i = i - 1;
}
}
}
}