动态规划(五):多重背包问题
一、多重背包问题
背包问题为有N种物品和一个最多能被重量为W 的背包。第i件物品的重量是weight[i],数量为Mi,得到的价值是value[i] ,没件物品只能使用一次,将这些物品装进背包,怎么装价值最高。
二、多重背包问题的解法
我们注意到01背包并没有要求物品不一样,因此我么可以把多重背包摊开,把Mi件第i中物品视作Mi种物品,没种只有一个,这就转换成一个01背包问题,具体的转化方式有两种
1、显示摊开
void test_multi_pack() { vector<int> weight = { 1, 3, 4}; vector<int> value = { 15, 20, 30}; vector<int> nums = { 2, 3, 2}; int bagWeight = 10; /-----将物品展开-----/ for (int i = 0; i < nums.size(); i++) { while (nums[i] > 1) { // nums[i]保留到1,把其他物品都展开 weight.push_back(weight[i]); value.push_back(value[i]); nums[i]--; } } /------标准01背包算法-----/ vector<int> dp(bagWeight + 1, 0); for(int i = 0; i < weight.size(); i++) { // 遍历物品 for(int j = bagWeight; j >= weight[i]; j--) { // 遍历背包容量 dp[j] = max(dp[j], dp[j - weight[i]] + value[i]); } for (int j = 0; j <= bagWeight; j++) { cout << dp[j] << " "; } cout << endl; } cout << dp[bagWeight] << endl; } int main() { test_multi_pack(); }
2、隐式展开
void test_multi_pack() { vector<int> weight = { 1, 3, 4}; vector<int> value = { 15, 20, 30}; vector<int> nums = { 2, 3, 2}; int bagWeight = 10; vector<int> dp(bagWeight + 1, 0); for(int i = 0; i < weight.size(); i++) { // 遍历物品 for(int j = bagWeight; j >= weight[i]; j--) { // 遍历背包容量 /----在01背包内部加一个循环,将同一件物品的k件进行展开----/ for (int k = 1; k <= nums[i] && (j - k * weight[i]) >= 0; k++) { // 遍历个数 dp[j] = max(dp[j], dp[j - k * weight[i]] + k * value[i]); } } // 打印一下dp数组 for (int j = 0; j <= bagWeight; j++) { cout << dp[j] << " "; } cout << endl; } cout << dp[bagWeight] << endl; } int main() { test_multi_pack(); }
for (int k = 1; k <= nums[i] && (j - k * weight[i]) >= 0; k++) { // 遍历个数 dp[j] = max(dp[j], dp[j - k * weight[i]] + k * value[i]); }
这段展开代码的含义:对于物品i,在背包容量足够的情况下,我可以选择放0-nums[i]件,dp[j]要记录价值最大的一种放置方法时的最大价值。由于背包容量j是从后向前遍历的,因此dp[j - k * weight[i]]是还没有经历过第i轮计算的,其内不可能还有i物品,因此dp[j - k * weight[i]] + k * value[i]就是背包内放了k件i物品时的最大价值。
下一篇:
责任链模式,建造者模式