在做題的時候,遇到一個 二維揹包問題,需要在重量不超過重量限制的情況下,使價值接近價值限制,但不能超過它。經過嘗試後發現,對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