在做题的时候,遇到一个 二维背包问题,需要在重量不超过重量限制的情况下,使价值接近价值限制,但不能超过它。经过尝试后发现,对G矩阵进行一些处理可以得出结果。
首先是从value[] 中选出一个不超过,但最接近valueLimit的值,记录它的位置,然后去G矩阵里从这个位置开始倒推,可以得到想要的解,即物品的位置。代码如下:
iimport java.util.Arrays;
import java.util.Scanner;
public class Knapsack {
private final static int MIN = Integer.MIN_VALUE;
public static void main(String[] args){
int wLimit = 8;
int vLimit = 13;
int[] w = {1,2,2,4,6};
int[] v = {2,3,5,6,7};
knapsackOptimal(wLimit, w,vLimit, v);
}
/**
* @param weightLimit 总重量限制
* @param weight 物品重量
* @param valueLimit 总价值限制
* @param value 物品价值
* 在总重量,和总价值不超出限制条件下,使得价值最大化
*/
public static void knapsackOptimal(int weightLimit, int[] weight, int valueLimit,int[] value) {
int n = weight.length;
int[] w = new int[n + 1];
int[] v = new int[n + 1];
//G[i][j]表示前i个物品能装入容量为j的背包中的最大价值
int[][] G = new int[n + 1][weightLimit + 1];
for (int i = 1; i < n + 1; i++) {
w[i] = weight[i - 1];
v[i] = value[i - 1];
}
//初始化values[0...c]=0————在不超过背包容量的情况下,最多能获得多少价值
//原因:如果背包并非必须被装满,那么任何容量的背包都有一个合法解“什么都不装”,这个解的价值为0,所以初始时状态的值也就全部为0了
int[] values = new int[weightLimit + 1];
//初始化values[0]=0,其它全为负无穷————解决在恰好装满背包的情况下,最多能获得多少价值的问题
//原因:只有容量为0的背包可以什么物品都不装就能装满,此时价值为0,其它容量背包均无合法的解,属于未定义的状态,应该被赋值为负无穷
/*for (int i = 1; i < values.length; i++) {
values[i] = MIN;
}*/
for (int i = 1; i < n + 1; i++) {
for (int t = weightLimit; t >= w[i]; t--) {
if ( (values[t] < values[t - w[i]] + v[i] ) ) {
values[t] = values[t - w[i]] + v[i];
G[i][t] = 1;
}
}
}
System.out.println("价值矩阵:"+Arrays.toString(values));
System.out.println("无价值限制下的最大价值:" + values[weightLimit]);
System.out.println("G矩阵: ");
for(int i=0;i<G.length;i++){
System.out.println(Arrays.toString(G[i]));
}
int index = getValueIndex(values,valueLimit);
System.out.println("满足价值限制条件下的最大价值:"+values[index]);
System.out.println("index"+index);
/*
输出顺序:逆序输出物品编号
注意:这里另外开辟数组G[i][v],标记上一个状态的位置
G[i][v] = 1:表示物品i放入背包了,上一状态为G[i - 1][v - w[i]]
G[i][v] = 0:表示物品i没有放入背包,上一状态为G[i - 1][v]
*/
System.out.println("输出物品位置: ");
int i = n;
// int j = weightLimit;//无限制下计算位置,从最后一列开始倒推
int j = index;//有限制下计算物品位置,从该行开始倒推
while (i > 0) {
if (G[i][j] == 1) {
System.out.print(i + " ");
j -= w[i];
}
i--;
}
}
//Finds the position of the value in the largest value array ( value[] )that
//is less than and closest to valueLimit .
private static int getValueIndex(int[] values,int valueLimit) {
int index = 0;
// TODO Auto-generated method stub
if(valueLimit-values[values.length-1]>0){
index = values.length-1;
}else{
for(int i=0;i<values.length;i++){
if(valueLimit-values[i]<0){
index = i-1;
break;
}if(valueLimit-values[i]==0){
index = i;
break;
}
}
}
return index;
}
}
样例运行结果
价值矩阵:[0, 2, 5, 7, 8, 10, 11, 13, 14]
无价值限制下的最大价值:14
G矩阵:
[0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 1, 1, 1, 1, 1, 1, 1, 1]
[0, 0, 1, 1, 1, 1, 1, 1, 1]
[0, 0, 1, 1, 1, 1, 1, 1, 1]
[0, 0, 0, 0, 0, 0, 1, 1, 1]
[0, 0, 0, 0, 0, 0, 0, 0, 0]
满足价值限制条件下的最大价值:13
index7
输出物品位置:
4 3 1