完全背包
有n
件物品和一个最多能背重量为w
的背包。第i
件物品的重量是weight[i]
,得到的价值是value[i]
。每件物品有无限个(也就是可以每个物品可以多次放入背包),求解将哪些物品装入背包里物品价值总和最大。
示例1:
输入:n = 3, w = 4, weight = [1, 3, 4], value = [15, 20, 30]
输出:60
解题思路
经典动态规划问题。完全背包和
二维数组解法
针对示例1,如果是01背包问题,可以得到的dp数组如下图所示:
从上图可以看出,dp[1][3]
是由dp[0][3]
和dp[0][0]
推导出来的,也就是说dp[i][j]
是由dp[i - 1][j]
和dp[i - 1][j - weight[i]]+ value[i]
推导出来的。从而可以得出结论在01背包中,当前状态只与上方和左上方的状态有关系。
如果是完全背包问题,可以得到的dp数组如下图所示:
从上图可以看出,dp[1][3]
是由dp[0][3]
和dp[1][0]
推导出来的,也就是说dp[i][j]
是由dp[i - 1][j]
和dp[i][j - weight[i]] + value[i]
推导出来的。从而可以得出结论在完全背包中,当前状态只与上方和左方的状态有关系。
于是,有
01背包的递推公式:
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]]+ value[i])
完全背包的递推公式:
dp[i][j] = max(dp[i - 1][j], dp[i][j - weight[i]]+ value[i])
对于完全背包dp数组的初始化,从上图来看还是有些复杂的。这可以通过给dp数组添加一行来解决初始化的麻烦,也就是从从没有物品时考虑。这样物品0的下标就从1开始了。
C++
class Solution { public: int completeBackpake(vector<int> weight, vector<int> value, int bagsize) { vector<vector<int>> dp(weight.size() + 1, vector<int>(bagsize + 1, 0)); for (int i = 1; i < weight.size() + 1; i++) { for (int j = weight[i - 1]; j < bagsize + 1; j++) { dp[i][j] = max(dp[i - 1][j], dp[i][j - weight[i - 1]] + value[i - 1]); } } return dp[weight.size()][bagsize]; } };
JavaScript
/** * @param {number[]} weight * @param {number[]} value * @param {number} bagsize */ // 二维数组 function completeBackpack(weight, value, bagsize) { const dp = Array(weight.length + 1).fill(0).map(item => Array(bagsize + 1).fill(0)); for (let i = 1; i < weight.length + 1; i++) { for (let j = weight[i - 1]; j < bagsize + 1; j++) { dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - weight[i - 1]] + value[i - 1]); } } return dp[weight.length][bagsize]; }
一维数组解法
0-1背包问题 和 完全背包问题 的一维数组解法主要是 遍历顺序不一样。
-
用滚动数组的方式优化0-1背包问题时,先正序遍历物品,再倒序遍历背包(必须先物品后背包)。 因为当前状态只与上方和左上方的状态有关系。递推公式为
dp[j] = max(dp[j], dp[j - weight[i]]+ value[i])
-
而用滚动数组的方式优化完全背包问题时,先正序遍历物品,再正序遍历背包(也可以先正序遍历背包,再正序遍历物品)。因为当前状态只与上方和左方的状态有关系。递推公式为
dp[j] = max(dp[j], dp[j - weight[i]]+ value[i])
C++
class Solution { public: int completeBackpake(vector<int> weight, vector<int> value, int bagsize) { vector<int> dp(bagsize + 1, 0); for (int i = 0; i < weight.size(); i++) { for (int j = weight[i]; j < bagsize + 1; j++) { dp[j] = max(dp[j], dp[j - weight[i]] + value[i]); } } return dp[bagsize]; } };
/** * @param {number[]} weight * @param {number[]} value * @param {number} bagsize */ // 一维数组 function completeBackpack(weight, value, bagsize) { const dp = Array(bagsize + 1).fill(0); for (let i = 0; i < weight.length; i++) { for (let j = weight[i]; j < bagsize + 1; j++) { dp[j] = Math.max(dp[j], dp[j - weight[i]] + value[i]); } } return dp[bagsize]; }