思路
01、完全、多重三种背包问题大杂烩,理论上分别套模版就行,但由于上限是1000,多重背包需要进行二进制优化,转为01背包问题解决。
C++ 代码
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 10000, V = 1010; // 1000 * log(1000) < 10000,N取10000,V取1010
int v[N], w[N], s[N], f[V]; // v为体积,w为价值,s为个数,f为体积小于等于j时的最大价值
int n, m;
int main()
{
scanf("%d%d", &n, &m);
int v0, w0, s0;
int cnt = 0; // 记录物品总数,即下标
for (int i = 1; i <= n; i++)
{
scanf("%d%d%d", &v0, &w0, &s0); // 输入某个物品的体积、价值、个数
if (s0 > 0) // 多重背包,二进制优化,转为01背包
{
for (int k = 1; k <= s0; k *= 2) // 二进制优化,将s0个物品拆分为log(s0)个物品(向上取整)
{
cnt++; // 下标加1
v[cnt] = k * v0;
w[cnt] = k * w0;
s[cnt] = -1; // 标记为-1,即01背包的情况
s0 -= k; // 剩余物品个数减去k
}
if (s0 > 0) // 剩余物品个数大于0,将剩余物品个数作为一个物品
{
cnt++;
v[cnt] = s0 * v0;
w[cnt] = s0 * w0;
s[cnt] = -1;
}
}
else // 01背包和完全背包的情况
{
cnt++;
v[cnt] = v0;
w[cnt] = w0;
s[cnt] = s0; // s0为-1表示01背包,s0为0表示完全背包
}
}
for (int i = 1; i <= cnt; i++) // 注意上限是cnt,拆分后n不是物品总数
{
if (s[i] == -1) // 01背包(包含拆分后的多重背包)
{
for (int j = m; j >= v[i]; j--) // 01背包,逆序遍历
f[j] = max(f[j], f[j - v[i]] + w[i]); // 状态转移方程相同
}
else // 完全背包
{
for (int j = v[i]; j <= m; j++) // 完全背包,顺序遍历
f[j] = max(f[j], f[j - v[i]] + w[i]); // 状态转移方程相同
}
}
printf("%d", f[m]);
return 0;
}