2

推荐背包九讲,非常棒。以下是个人对其前四讲内容的梳理和Java实现,用于快速回顾知识点。

01背包

问题:

 N件物品,一个容量M的背包。A[]是物品体积,V[]是物品价值,求背包能装物品的最大价值。

思路:

dp[i][j]表前i件物品在容量最大为j的限制下能得到的最大价值。

图片描述

    public int pack01Solution1(int m,int A[],int V[] ){
        int[][] dp = new int[A.length+1][m+1];
        for(int i=0;i<A.length;i++){
            for(int j=0;j<=m;j++){
                if(A[i]>j)dp[i+1][j] = dp[i][j];
                else dp[i+1][j] = Math.max(dp[i][j],dp[i][j-A[i]]+V[i]);
            }
        }
        return dp[A.length][m];
    }

时间复杂度O(NM),空间复杂度O(NM)。

空间优化为O(M):

    public int pack01Solution2(int m,int A[],int V[] ){
        int[]dp = new int[m+1];
        for(int i=0;i<A.length;i++){
            for(int j=m;j>=A[i];j--){
                dp[j] = Math.max(dp[j],dp[j-A[i]]+V[i]);
            }
        }
        return dp[m];
    }

可以把01背包中对一件物品的处理抽取为方法:

    public void ZeroOnePack(int cost,int value,int[] dp,int m){
        for(int j=m;j>=cost;j--)
            dp[j] = Math.max(dp[j],dp[j-cost]+value);
    }

初始化的细节问题:

clipboard.png

完全背包问题

问题:

N种物品,背包容量为M,每种无限件,给出物品体积和价值A[]、V[],求最大价值。

思路:

dp[i][j] = max{dp[i-1][j-k*A[i]]+k*V[i]|0<=k*A[i]<=M}

时间复杂度:有O(NM)个状态,每个状态求解时间O(M/A[i]),总时间O(Σ(M/A[i])*M).

转化为01背包问题:

-第i种物品转化为M/A[i]件体积和价值不变的物品,复杂度不变
-第i种物品表示成2^k件物品的总和,A[i]*(2^k)<=M,每种物品可拆成O(logM/A[i])件物品
-一维数组实现的**O(NM)**算法:dp[i][j] = max{dp[i-1][j],dp[i][j-A[i]]+V[j]}
    public int packComSolution(int m,int[] A,int[] V){
        int[] dp = new int[m+1];
        for(int i=0;i<A.length;i++){
            for(int j=A[i];j<=m;j++)
                dp[j] = Math.max(dp[j],dp[j-A[i]]+V[i]);
        }
        return dp[m];
    }

其中处理一种完全背包的物品可以抽取方法:

    public  void CompletePack(int cost,int value,int[] dp,int m){
        for(int j=cost;j<=m;j--)
            dp[j] = Math.max(dp[j],dp[j-cost]+value);
    }

多重背包

问题:

在01背包基础上,增加条件每件物品的可取件数n[];

思路:

转化成01背包,每种物品的n[i]都可以用2^0,2^1,..2^(k-1),n-(2^k-1)中若干数的和表示,k是满足n[i]-2^k+1>0的最大整数。
第i种物品分成了O(log n[i])种物品,复杂度从O(M*Σn[i])变为O(M* Σlog n[i])
 public void packMutiSolutionk(int m,int[] A,int[] V ,int[] N){
        int[] dp = new int[m+1];
        for(int i=0;i<A.length;i++){
            MutiPack(A[i],V[i],N[i],dp,m);
        }
    }
    public void MutiPack(int cost, int value, int amount, int[] dp, int m){
        if(cost*amount>=m){
            CompletePack(cost,value,dp,m);
            return;
        }
        int s=1;
        while(s<amount){//条件:amount-s>0
            ZeroOnePack(s*cost,s*value,dp,m);
            amount -= s;
            s *= 2;
        }
        ZeroOnePack(amount*cost,amount*value,dp,m);
    }

混合背包

clipboard.png


swan
16 声望0 粉丝