0-1背包/完全背包问题优化

上一篇博客中写道“0-1背包问题”,同时也给出了状态转移方程,但是空间复杂度还是比较高的。
但是上一篇博客中还没有给出相应的代码,所以今天这篇博客就将其完善一下,并且优化一下空间复杂度。
附上一篇博客链接:彻底搞懂0-1背包问题(动态规划)

0-1背包问题的优化
public class test3 {
    public static void main(String[] args) {
    	//这边需要假设第0个物品的重量和价值为0,因为我们是从第1个物品开始算的,这一点很重要
        int[] weight = {0,4,3,1};
        int[] value = {0,30,20,15};
        int nums = weight.length - 1; //物品的数量
        int bag_weight = 4; //背包的重量
        int[][] dp = new int[nums + 1][bag_weight+1];
        //先遍历物品的的数量
        for(int i=1;i<=nums;i++){
        	//遍历背包的重量
            for(int j=1;j<=bag_weight;j++){
                if(j >= weight[i]){
                //决策:要么拿,要么不拿,取两者最大值
                    dp[i][j] = Math.max(dp[i-1][j],dp[i-1][j-weight[i]] + value[i]);
                }else{
                    dp[i][j] = dp[i-1][j];
                }
            }
        }
        System.out.println(dp[nums][bag_weight]);//输出35
    }
}

在这里插入图片描述

  • 我们可以看到更新后的最大价值 dp[i][j] 基于两个决策,一个是在当前行,我不拿,那么最大价值就是由前一行的最大值直接传递过来;如果拿,那么需要减去这个物品的重量的背包所能装的最大价值+这个物品的价值;然后两者取最大值。
  • 很明显我们当前的这个最大值dp[i][j]是根据当前状态的上一行的左边更新过来的(如果当前状态是上图黄色部分,那就是根据上图中的绿色部分更新最大值)。并且很重要的一点就是这个最大值是根据旧值更新过来的,所以我们在代码中必须让 j 以递减的形式更新,以保证能够取到上一行的前面的旧值。
public class test3 {
    public static void main(String[] args) {
        int[] weight = {0,4,3,1};
        int[] value = {0,30,20,15};
        int nums = weight.length - 1; //物品的数量
        int bag_weight = 4; //背包的重量
        int[] dp = new int[bag_weight+1];
        for(int i=1;i<=nums;i++){
        //这边从右往左更新,j需要递减
            for(int j=bag_weight;j>=weight[i];j--){
                dp[j] = Math.max(dp[j],dp[j-weight[i]]+value[i]);
            }
        }
        System.out.println(dp[bag_weight]);
    }
}

完全背包问题的优化
  • 完全背包和 0-1背包不同的地方在于物品的数量是无限个,你可以重复拿。但是思路也是一样的,只有两种大的决策:要么拿这个物品,要么不拿这个物品。如果拿这个物品,判断拿几个这个物品所产生的的价值是最大的。 所以我们很容易得到以下的代码。
public class wanquan_bag {
    public static void main(String[] args) {
        int[] weight = {0,4,3,1};
        int[] value = {0,30,20,15};
        int nums = weight.length - 1; //物品的数量
        int bag_weight = 4; //背包的重量
        int[][] dp = new int[nums + 1][bag_weight+1];
        for(int i=1;i<=nums;i++){
            for(int j=1;j<=bag_weight;j++){
            //k表示我们可以拿几个这个物品,但是要保证所拿物品的重量小于当前背包的重量
                for(int k=0;k*weight[i]<=j;k++){
                    dp[i][j] = Math.max(dp[i-1][j],dp[i][j-k*weight[i]]+k*value[i]);
                }
            }
        }
        System.out.println(dp[nums][bag_weight]);//输出60
    }
}
//************************************//
//或者使用以下代码
public class wanquan_bag {
    public static void main(String[] args) {
        int[] weight = {0,4,3,1};
        int[] value = {0,30,20,15};
        int nums = weight.length - 1; //物品的数量
        int bag_weight = 4; //背包的重量
        int[][] dp = new int[nums + 1][bag_weight+1];
        for(int i=1;i<=nums;i++){
            for(int j=1;j<=bag_weight;j++){
                if(j >= weight[i]){
                    dp[i][j] = Math.max(dp[i-1][j],dp[i][j-weight[i]]+value[i]);
                }
            }
        }
        System.out.println(dp[nums][bag_weight]);
    }
}

在这里插入图片描述

  • 在这里我们也能很容易发现当前的dp[i][j]最大值就取决于上图的绿色部分。但是这里和 0-1 背包不同的优化方式,我们要的是新值,而不在是旧值。所以我们在优化的时候需要从左往右更新,j 是递增的。
public class wanquan_bag {
    public static void main(String[] args) {
        int[] weight = {0,4,3,1};
        int[] value = {0,30,20,15};
        int nums = weight.length - 1; //物品的数量
        int bag_weight = 4; //背包的重量
        int[] dp = new int[bag_weight+1];
        for(int i=1;i<=nums;i++){
        	//这边从左往右更新,j需要递增
            for(int j=1;j<=bag_weight;j++){
                if(j >= weight[i]){
                    dp[j] = Math.max(dp[j],dp[j-weight[i]]+value[i]);
                }
            }
        }
        System.out.println(dp[bag_weight]);
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值