问题描述:给定n种物品和一背包。物品i的重量是wi,其价值为vi,背包的容量为C。问应如何选择装入背包的物品,使得装入背包中物品的总价值最大?
0-1背包问题是一个特殊的整数规划问题。
0-1背包问题的目标函数和约束条件如下:
其最优值为m(i,j),即m(i,j)是背包容量为j,可选择物品为i,i+1,…,n时0-1背包问题的最优值。由0-1背包问题的最优子结构性质,可以建立计算m(i,j)的递归式如下。
例如:有编号分别为1,2,3,4,5的五件物品,它们的重量分别是2,2,6,5,4,它们的价值分别是6,3,5,4,6,现在给你个承重为10的背包,如何让背包里装入的物品具有最大的价值总和?
代码如下:
public class test3_9 {
public static void knapsack(int[] v,int[] w,int c,int[][] m){
int n = v.length-1;
//若w[n]>c表示第n个物品重量大于当前剩余容量c,则选择不装(jMax=c,则第一个for循环m[n][0]~m[n][c]=0)
int jMax = Math.min(w[n]-1, c);
//1.求m[n][j]
for(int j=0;j<=jMax;j++)
m[n][j] = 0;
for(int j=w[n];j<=c;j++)
m[n][j] = v[n];
//2.求m[i][j]
for(int i=n-1;i>0;i--){ //从倒数第二个物品向前迭代,共迭代n-2次
jMax = Math.min(w[i]-1, c);
for(int j=0;j<=jMax;j++)
m[i][j] = m[i+1][j];
for(int j=w[i];j<=c;j++)
m[i][j] = Math.max(m[i+1][j], m[i+1][j-w[i]]+v[i]);
}
//3.求m[0][c]
m[0][c] = (w[0]>c)? m[1][c]: Math.max(m[1][c], m[1][c-w[0]]+v[0]);
}
public static void traceback(int[][] m,int[] w,int c,boolean[] x){
int n = w.length-1;
for(int i=0;i<n;i++)
if(m[i][c]==m[i+1][c]) //若相等则说明,第i个物品没有放到背包里
x[i] = false;
else{
x[i] = true;
c -= w[i];
}
x[n] = (m[n][c]>0)? true:false; //最后一个物品有价值说明最后一个物品放进了背包
}
public static void main(String[] args) {
int[] w = {2,2,6,5,4};
int[] v = {6,3,5,4,6};
int n = v.length; //物品的数量
boolean[] x = new boolean[n];
int c = 10;
int[][] m = new int[n][c+1]; //m[][0]~m[][c]
knapsack(v,w,c,m);
traceback(m,w,c,x);
for(int i=0;i<v.length;i++){
for(int j=0;j<=c;j++)
System.out.print(m[i][j]+" ");
System.out.println();
}
for(int i=0;i<n;i++)
System.out.println("第"+(i+1)+"个物品是否放入背包:"+x[i]+" ");
}
}
输出结果:
第1个物品是否放入背包:true
第2个物品是否放入背包:true
第3个物品是否放入背包:false
第4个物品是否放入背包:false
第5个物品是否放入背包:true
总结:0-1背包问题程序求解三大步骤如下
①求m[n][j]
②求m[i][j]
③求m[0][c]
时间复杂度为:O(n*c)。