动态规划---0-1背包问题

时间:2022-03-11 18:44:15

问题描述:给定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