题目链接:点击这里
多重背包问题描述:有
种物品和一个容量为
的背包。第
种物品最多有
件可用,每件耗费的空间是
,价值是
。求解将哪些物品装入背包可使这些物品的耗费的空间总和不超过背包容量,且价值总和最大。
一种好想好写的基本方法是转化为 01 背包求解:把第 种物品换成 件 01 背包中的物品,则得到了物品数为 的 01 背包问题。直接求解之,复杂度仍然是 。
但是我们期望将它转化为 01 背包问题之后,能够像完全背包一样降低复杂度。
仍然考虑二进制的思想,我们考虑把第 种物品换成若干件物品,使得原问题中第 种物品可取的每种策略——取 件——均能等价于取若干件代换以后的物品。另外,取超过 件的策略必不能出现。
方法是:将第 种物品分成若干件 01 背包中的物品,其中每件物品有一个系数。这件物品的费用和价值均是原来的费用和价值乘以这个系数。令这些系数分别为 ,且 是满足 的最大整数。
例如,如果 为 ,则相应的 ,这种最多取 件的物品应被分成系数分别为 的四件物品。
分成的这几件物品的系数和为 ,表明不可能取多于 件的第 种物品。另外这种方法也能保证对于 间的每一个整数,均可以用若干个系数的和表示。(算法正确性的证明可以分 和 两段来分别讨论得出,感兴趣的可以尝试一下)
这样就将第 种物品分成了 种物品,将原问题转化为了复杂度为 的 01 背包问题,是很大的改进。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn = 2010;
int dp[maxn];
struct Good {
int v, w;
};
int main()
{
vector<Good> goods;
int N, V;
scanf("%d%d", &N, &V);
for(int i = 0; i < N; ++i)
{
int v, w, s; //v物品体积,w物品价值,s物品数量
scanf("%d%d%d", &v, &w, &s);
for(int k = 1; k <= s; k *= 2)
{
s -= k;
goods.push_back({v*k, w*k});
}
if(s>0) goods.push_back({v*s, w*s});
}
for(auto it : goods)
{
for(int j = V; j >= it.v; --j)
{
dp[j] = max(dp[j], dp[j-it.v]+it.w);
}
}
printf("%d", dp[V]);
return 0;
}