dp分析方式与完全背包一致,因此朴素版本写法基本一致
但是优化方式不同,原因如下:
在完全背包中,优化的依据是:
f[i, j] = max(f[i - 1, j], f[i - 1, j - v] + w, f[i - 1, j - 2v] + 2w, ..., f[i - 1, j - kv] + kw) ①
此时k的最大取值为 j / v (下取整),此时令 j = j - v;
f[i, j - v] = max(f[i - 1, j - v], f[i - 1, j - 2v] + w, f[i - 1, j - 3v] + 2w, ...., f[i - 1, j - kv] + (k - 1) w) ②
因为k最大取值即为 j / v(下取整),因此相当于最多选取 k - 1件 i 物品,因此可以带入①式,即完全背包的优化方式。
但是在多重背包中,由于第 i 件物品只有 s[ i ] 件,因此最多可以选取s[ i ]件(假如 j 足够大),如果按照完全背包的优化方式优化,发现最后一项其实是多出来的,在取max的操作中,多出的这一项无法判断是否影响取值结果,所以不能按照完全背包的优化方式优化。
多重背包优化方式:
将 s[ i ] 件物品进行打包,分为1 + 2 + 4 + 8 + ... + 2 ^ k + c 。
这样是可以正确表示出 1 ~ s[ i ] 中的任何一个数的,证明如下:
1, 2 可以表示出 1 ~ 3 的任何一个数的,因此1, 2, 4 可以表示出1 ~ 7 中的任何一个数(相当于在1 ~ 3 的基础上加 4)同理1, 2, 4, 8 可以表示出1 ~ 15中的任何一个数,递推可得 1 + 2 + 4 + ... + 2 ^ k 可以表示出 1 ~ 2 ^ (k + 1) - 1 中的任何一个数,如果此时2 ^ (k + 1) - 1 < s[ i ] < 2^ (k + 2) - 1,c即为s[ i ] - 2 ^ (k + 1) - 1 。
此时将这s[ i ] 件物品打包成若干件物品,每份物品重量和价值即为 v * cnt, w * cnt 。
接下来就可以按0 - 1背包问题的方法解决。时间复杂度 O( N * log N)
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 25000;
int n, m;
int v[N], w[N];
int f[N];
int main()
{
cin >> n >> m;
int cnt = 0;
for(int i = 1;i <= n;i ++ )
{
int a, b, c;
cin >> a >> b >> c;//处理输入,a为体积,b为价值,c为个数
int k = 1;
while(k <= c)//打包
{
cnt ++;
v[cnt] = k * a;
w[cnt] = k * b;
c -= k;
k *= 2;
}
if(c > 0)
{
cnt ++;
v[cnt] = a * c;
w[cnt] = b * c;
}
}
n = cnt;//此时的物品个数是cnt
for(int i = 1;i <= n;i ++ )//01背包优化版本
for(int j = m;j >= v[i];j --)
f[j] = max(f[j], f[j - v[i]] + w[i]);
cout << f[m] << endl;
return 0;
}