分享完使用动态规划解决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。