完全背包问题

        分享完使用动态规划解决01背包问题及使用滚动数组对优化01背包问题后,这次分享了使用动态规划解决完全背包问题。
        01背包问题和完全背包问题的区别在于01背包问题中物品只能取一次,而完全背包问题物品可以取多次,在上篇文章中使用滚动数组(就是一个一维数组 )对优化01背包问题时,要求在遍历背包容量时,必须要倒序遍历,原因时为了避免添加同一个物品,而完全背包问题允许添加重复物品,因此,将遍历背包容量的循环从倒序遍历修改为正序遍历,即从01背包问题转化为完全背包问题。下面给出使用滚动数组优化后的完全背包问题完整代码。

完全背包问题

你有一个背包,最多能容纳的体积是V。现在有n种物品,每种物品有任意多个,第i种物品的体积为𝑣𝑖,价值为𝑤𝑖。
(1)求这个背包至多能装多大价值的物品?
(2)若背包恰好装满,求至多能装多大价值的物品?

示例2
输入:8,3,[[3,10],[4,15],[5,20]]
返回值:[30,30]
说明:无法恰好装满背包。

import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param v int整型 
     * @param n int整型 
     * @param nums int整型ArrayList<ArrayList<>> 
     * @return int整型ArrayList
     */
    public ArrayList<Integer> knapsack (int v, int n, ArrayList<ArrayList<Integer>> nums) {
        // write code here
        int[] dp = new int[v + 1];
        int[] dp1 = new int[v + 1];
        for(int i = 1;i <= v;i++){
            dp1[i] = Integer.MIN_VALUE;
        }
        for(int i = 0;i < nums.size();i++){
            int weight = nums.get(i).get(0);
            int value = nums.get(i).get(1);
            for(int j = 0;j <= v;j++){
                if(j >= weight){
                    dp[j] = Math.max(dp[j], dp[j - weight] + value);
                    if(dp1[j - weight] != Integer.MIN_VALUE){
                        dp1[j] = Math.max(dp1[j], dp1[j - weight] + value);
                    }
                }
            }
        }
        ArrayList<Integer> ans = new ArrayList<>();
        ans.add(dp[v]);
        ans.add(dp1[v] == Integer.MIN_VALUE ? 0 : dp1[v]);
        return ans;
    }
}

        上述定义了两个一维dp数组,也成为滚动数组,dp用于计算这个背包至多能装多大价值的物品,dp1用于计算若背包恰好装满,求至多能装多大价值的物品。前面的问题即是完全背包问题,就不再详细展开说明,后面的问题将详细展开说明。首先要明白dp1[i]的含义为背包容量为i时恰好能装满背包的最大价值。dp1[0]一定要初始化为0,而后将其他元素初始化为Integer.MIN_VALUE,为什么这样初始化,后面会说到。
        以上述示例说明代码的详细执行流程。物品和背包信息如下:
在这里插入图片描述

        dp1数组初始化如下:
在这里插入图片描述
        首先遍历物品0,背包容量从0-8,上述代码中j就表示背包容量,j > 物品0的体积时,才会进入if,显然背包容量为0,1,2时,都不会进入if判断,当背包容量为3时,j等于物品0的体积,并且j - 物品0体积3时刚好等于dp[0],所以dp[3] = Math.max(dp[3],dp[0] + 3],最初dp[3] = Integer.MIN_VALUE,最后dp[3]等于3。背包容量刚好装下物品0,所以这里就解释为什么dp[0]要等于0,而不是其他值。其他元素初始化为Integer.MIN_VALUE原因是上述求max时,要求dp[3]的初始值要小于0,否则对求max有影响,其实也没必要初始化为最小值,小于的值就可以。之后背包容量为4、5时,j - 物品0的重量都等于Integer.MIN_VALUE,表示无法刚好装满,当背包容量为6时,dp[6] = Math(dp[6],dp[3] + 3],dp[6] = 6,之后背包容量为7和8,j - 物品0的重量都等于Integer.MIN_VALUE。遍历完物品0,得到dp1数组如下:
在这里插入图片描述
        接着遍历物品1,背包容量为1,2,3时,背包容量j都是小于物品1的体积,不进入if,背包容量为4时,等于物品1的体积,同时j - 物品1的体积刚好等于dp[0]等于0,不等于Integer.MIN_VALUE,所以dp[4] = 15,背包容量为5,6,7时,背包容量均有剩余,j - 物品1的价值等于Integer.MIN_VALUE,无法刚好装满背包,当背包容量为8时,dp[8] = dp[4] + 15 = 30。物品1遍历完成后,dp1数组如下:
在这里插入图片描述
        遍历物品2就不展开说了,遍历完成物品2后,dp1数组如下:
在这里插入图片描述
        值得注意的是,问题1要求背包价值最大,不一定刚好装满背包,问题2,刚好装满背包,价值不一定是最大的,这题刚好两个问题的最优值均为30。

参考:带你学透完全背包问题! 和 01背包有什么差别?遍历顺序上有什么讲究?

  • 31
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值