有一个背包,容量为c。
有n个物体X0,X1,X2,...,Xn-1,重量分别为W0,W1,W2,...,Wn-1,价值分别为V0,V1,V2,...,Vn-1。
将这n个物体中的任意几个放入背包,使得在不超过背包容量的情况下,背包中的物体总价值最大。
数学描述:
c>0
Wi>0, Vi>0, 0<=i<=n-1
求:[X0,X1,X2,...,Xn-1],其中Xi=0或1,使得X0*V0+X1*V1+...+Xn-1*Vn-1最大
约束条件:X0*W0+X1*W1+...+Xn-1*Wn-1 <= c
(1)最优子结构
假设[X0,X1,X2,...,Xn-1]是 0~n-1个物体且最大值为c 的最优解,
那么[X0,X1,X2,...,Xn-2]是 0~n-2个物体且最大值为c-(Xn-1*Wi-1) 的最优解。可用反证法证明。
(2)建立递归关系
以max[i][j]作为 0~i个物体且最大容量为j 的最优解,易知max的维度为 n*(c+1)
当i=0时:如果 j<W0,max[i][j]=0
如果 j>=W0,max[i][j]=Vi
当i>=0时:如果 j<Wi,max[i][j]=max[i-1][j]
如果 j>=Wi,max[i][j]=MAX(max[i-1][j], max[i-1][j-Wi]+Vi)。分为包含Xi和不包含Xi两种。
(3)自底向上,计算各个子问题的最优解
详细情况(2)中已描述
(4)求解整个问题的最优解
max[n-1][c]即是整个问题的最优解,即最大价值。
从max[n-1][c]开始向前追溯,如果 max[i][j] == max[i - 1][j],就是不包含Xi,否则就是包含Xi。
代码:
package test.xue.alg.Dynamic;
import java.util.LinkedList;
/**
* 0-1背包问题
* n个物体 : X0,X1,X2,...,Xn-1
* 重量 : W0,W1,W2,...,Wn-1
* 价值 : V0,V1,V2,...,Vn-1
* 背包容量:c
* 将n个物体中的任意个放入背包,使得背包中的物体总重量最大
*/
public class MaxWeight {
public static int mw(int[] W, int[] V, int c) {
int n = W.length;
int[][] max = new int[n][c + 1];
// i=0时
for (int j = 0; j < W[0]; j++) {
max[0][j] = 0;
}
for (int j = W[0]; j <= c; j++) {
max[0][j] = V[0];
}
int m, tmp;
for (int i = 1; i < n; i++) {
for (int j = 0; j <= c; j++) {
m = max[i - 1][j];
if (j >= W[i]) {
tmp = max[i - 1][j - W[i]] + V[i];
if (tmp > m) {
m = tmp;
}
}
max[i][j] = m;
}
}
traceback(W, c, max);
return max[n - 1][c];
}
public static void traceback(int[] W, int c, int[][] max) {
int n = W.length;
int i = n - 1, j = c;
LinkedList<Integer> list = new LinkedList<Integer>();
for (; i > 0; i--) {
if (max[i][j] != max[i - 1][j]) {// 如果最大值为j的时候,有没有Xi价值都一样,那就是没有Xi
list.addFirst(i);
j -= W[i];
}
}
if (j >= W[0])
list.addFirst(0);
System.out.println(list);
}
public static void main(String[] args) {
int[] W = { 2, 2, 6, 5, 4 }; // 重量
int[] V = { 6, 3, 5, 4, 6 }; // 价值
int c = 10; // 背包容量
int max = mw(W, V, c);
System.out.println("max : " + max);
}
}
输出:
[0, 1, 4]
max : 15
意为:将X0,X1,X4装入背包时的总价值最大,为15.