问题描述:给定n种物品和一个背包。物品i的重量是wi,其价值为vi,背包的容量为c。问应如何选择装入背包中的物品,使得装入背包中物品的总价值最大?
记m[i][j],前i件物品,放入背包容量为j的背包时问题的最优值。
递归式:
当j>=w[i]时:
m[i][j]=max{m[i-1][j], m[i-1][ j-w[i] ]+v[i]}
当j < w[i]时:
m[i][j] =m[i-1][j]
算法实现:
public class KnapsackProblem {
/* * w[]:物品重量,下标从0开始,w[0]代表第1件物品重量 * v[]:物品价值,下标从0开始。 * c:背包容量 * m[i,j]:下标从1开始,前i件物品放入容量为j的背包可以获得的最大价值 */
public int[][] knapsack(int[] w,int[] v,int c,int n){
int i,j;
int[][] m= new int[n+1][c+1];
for(i=0;i<=n;i++)//初始化
m[i][0]=0;
for(j=0;j<=c;j++)
m[0][j]=0;
for(i=1;i<=n;i++)
for(j=1;j<=c;j++){
if(j<w[i-1])//背包容量没有第i件物品重量大,放不下
m[i][j]=m[i-1][j];
else{
if(m[i-1][j]>=m[i-1][j-w[i-1]]+v[i-1])
m[i][j]=m[i-1][j];
else
m[i][j]=m[i-1][j-w[i-1]]+v[i-1];
}
}
return m;
}
// 逆推法求出最优解
public int[] traceBack(int[][] m,int[] w,int c,int n){
int[] x = new int[n];//x[i]表示第i-1件物品是否被选中
for(int i=n;i>0;i--)
if(m[i][c]>m[i-1][c]){
x[i-1]=1;//第i件物品被选中,记为1
c-=w[i-1];//剩余背包容量
}else
x[i-1]=0;
return x;
}
public static void main(String[] args) {
int n=5; //物品个数
int c=10;//背包容量
int[] w = new int[]{4,2,6,5,4};//每个物品重量
int[] v = new int[]{6,3,5,4,6};//每个物品价值
KnapsackProblem k = new KnapsackProblem();
int[][] m=k.knapsack(w, v, c, n);
System.out.println("最大值:"+m[n][c]);
int[] x = k.traceBack(m, w, c, n);
System.out.print("被选中的物品:");
for(int i=0;i<x.length;i++)
System.out.print(x[i]+" ");
}
}
最大值:15
被选中的物品:1 1 0 0 1