背包问题

在动态规划中,背包问题是一个经典问题,因此,在这里对背包问题做一个小小的总结。

背包问题大致概述:

给定一个背包,容量为V,现有N种物品,求出在背包容量范围内,按照某种策略放入,能获得的最大价值是多少

背包问题主要分为三个类型:

(设置dp[i][j]来表示放入第i种物品,当前容量为j时产生的最大价值)

0 1背包问题

对于每种物品,只有一个,要么放进背包,要么不放入
故对于当前dp[i][j],首先判断 weight[i-1]>j(因为i,j都是是从1开始循环,所以这里是i-1,后同) 是否成立,即判断当前物品是不是超过了背包当前的容量

1.若是超过了当前的容量,则无法放入第i种物品
dp[i][j]=dp[i-1][j];

2.若是可以放入,则当前最大价值是不放入与放入第i种物品两种情况之间取最大值
dp[i][j]=Math.max(dp[i-1][j],dp[i-1][j-weight[i-1]]+value[i-1]);
因为放入第i种物品之前,需要预留第i种物品的空间大小,即为weight[i-1]
故放入第i种物品是:dp[i-1][j-weight[i-1]]+value[i-1]

多重背包问题

多重背包即对于每种物品,定义了它的数量为num[i-1]个,所以,和上面的0 1背包问题不同之处在于:对于第i个物品,若是选择放入,即放入的只是一个了,而是多个,至于具体放多少个,要通过计算比较来完成
即因为当前背包的容量,不一定能放下所有的第i种物品,所以需要通过:j/wieght[i-1]计算出当前第i种物品可以放入的数量,再与i物品本来的数量两者之间取最小值即可
即 int maxNum = Math.min(num[i - 1], j / weight[i - 1]);
故可以将第i种物品放入背包时,为:

                    for (int k = 0; k < maxNum + 1; k++) {
                        dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - k * weight[i - 1]] + k * value[i - 1]);
                    }

对于放入多少个我们并不知道,所以使用循环遍历取最大值

完全背包问题

完全背包问题即是:物品的数目不限,可以放入背包能容纳的最大限度

因为是放进任意个物品,注意这里当考虑放入一个物品 i 时应当考虑还可能继续放入 i,
所以下面的放入时的最大值情况是dp[i][j-weight[i-1]]+value[i-1]
dp[i-1][j]表示不放入,,dp[i][j-weight[i-1]]+value[i-1]表示放入第i件物品后还可以继续放入

package demo.lyq;


//动态规划之背包问题
public class DP_Packge {

    /**
     * 0 1背包问题 每种物品只有一个,要么放进背包,要么不放入
     * @param V 背包容量
     * @param N 物品种类数
     * @param weight 每种物品的重量
     * @param value 每种物品的价值
     * @return
     */
    public static int ZeroPackge(int V,int N,int[] weight,int[] value)
    {
        //新建一个二维数组dp,用于表示当前状态下的最大价值,dp[i][j]表示在当前放入i件物品,背包容量为j的情况下的最大价值
        int[][]dp=new int[N+1][V+1];
        for(int i=1;i<N+1;i++)
        {
            for(int j=1;j<V+1;j++)
            {
                if(weight[i-1]>j)//对于物品重量大于当前背包容量而放不下的情况,只能是选择不放入这次的物品
                {
                    dp[i][j]=dp[i-1][j];
                }
                //否则,对于这次的物品选择放入进去,比较与不放入之间的值,取较大的那种情况
                //dp[i-1][j]表示不放入,,dp[i-1][j-weight[i-1]]+value[i-1]表示放入第i件物品
                dp[i][j]=Math.max(dp[i-1][j],dp[i-1][j-weight[i-1]]+value[i-1]);
            }
        }
        return dp[N][V];

    }


    /**
     * 多重背包问题 每种物品有限定的数量
     * @param V 背包容量
     * @param N 物品种类数
     * @param weight 每种物品的重量
     * @param value 每种物品的价值
     * @param num 每种物品的最大使用个数
     * @return
     */
    public static int MultiPackge(int V,int N,int[] weight,int[] value,int[] num)
    {
        //新建一个二维数组dp,用于表示当前状态下的最大价值,dp[i][j]表示在当前放入i件物品,背包容量为j的情况下的最大价值
        int[][]dp=new int[N+1][V+1];
        for(int i=1;i<N+1;i++) {
            for (int j = 1; j < V + 1; j++) {
                if (weight[i - 1] > j)//对于物品重量大于当前背包容量而放不下的情况,只能是选择不放入这次的物品
                {
                    dp[i][j] = dp[i - 1][j];
                } else {
                    //考虑物品的件数限制,maxNum代表当前可以使用的物品的最大数量
                    // 要么是当前的容量/当前物品的重量计算出来的最大容量
                    int maxNum = Math.min(num[i - 1], j / weight[i - 1]);
                    for (int k = 0; k < maxNum + 1; k++) {
                        dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - k * weight[i - 1]] + k * value[i - 1]);
                    }

                }
            }
        }
        return dp[N][V];
    }




    /**
     * 完全背包问题,对于每种物品,可以放进任意个,只要背包能装下
     * @param V 背包容量
     * @param N 物品种类数
     * @param weight 每种物品的重量
     * @param value 每种物品的价值
     * @return
     */
    public static int CompletePackge(int V,int N,int[] weight,int[] value)
    {
        //新建一个二维数组dp,用于表示当前状态下的最大价值,dp[i][j]表示在当前放入i件物品,背包容量为j的情况下的最大价值
        int[][]dp=new int[N+1][V+1];
        for(int i=1;i<N+1;i++)
        {
            for(int j=1;j<V+1;j++)
            {
                if(weight[i-1]>j)//对于物品重量大于当前背包容量而放不下的情况,只能是选择不放入这次的物品
                {
                    dp[i][j]=dp[i-1][j];
                }
                //否则,对于这次的物品选择放入进去,比较与不放入之间的值,取较大的那种情况
                //因为是放进任意个物品,注意这里当考虑放入一个物品 i 时应当考虑还可能继续放入 i,
                // 所以下面的放入时的最大值情况是dp[i][j-weight[i-1]]+value[i-1]
                //dp[i-1][j]表示不放入,,dp[i][j-weight[i-1]]+value[i-1]表示放入第i件物品后还可以继续放入
                dp[i][j]=Math.max(dp[i-1][j],dp[i][j-weight[i-1]]+value[i-1]);
            }
        }
        return dp[N][V];
    }


}

待补充与简化

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值