开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 13 天,点击查看活动详情
今天进行多重背包模型最后一题。
多重背包 III
题目描述
有 种物品和一个容量是 的背包。
第 种物品最多有 件,每件体积是 ,价值是 。
求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。
输入格式
第一行两个整数,,用空格隔开,分别表示物品种数和背包容积。
接下来有 行,每行三个整数 ,用空格隔开,分别表示第 种物品的体积、价值和数量。
输出格式
输出一个整数,表示最大价值。
数据范围
输入样例
4 5
1 2 3
2 4 1
3 4 3
4 5 2
输出样例:
10
题目分析
本题是楼天城 "男人八题" 中的一道题目,解法为单调队列优化多重背包。
相较于昨天的多重背包题目,本题数据范围再次扩大,即使是 的算法也无法通过全部样例。
首先我们将多重背包的转移方程列出, 表示从前 件物品中选,总体积不大于 的最优解。
其中 。
经过观察可以发现,在不考虑偏移量 的情况下, 是数量为 的相邻 中的最大值。
于是可以使用滑动窗口进行优化。
考虑到偏移量 的存在,由于其随着 中 距离初始点 距离越近所加的 越大,且每相隔一个 便增加一个 ,而我们仅考虑目标项中的最大值的下标而不需知道具体数值,则可以采取变加为减的方法,每距离 多一个 便多减一个 。
然后我们枚举对于物品 的体积 的所有余数 ,便可得到 。
如果尝试一维数组,发现每次枚举余数会已经改变 的值,所以需要多开一维数组存第 层的值。
最终复杂度可以控制到 。
Accept代码 O(NV)
#include <bits/stdc++.h>
using namespace std;
const int N = 20010;
int f[N], g[N], q[N];
int main()
{
int n, m;
cin >> n >> m;
for (int i = 0; i < n; i ++)
{
int v, w, s;
cin >> v >> w >> s;
memcpy(g, f, sizeof f);
for (int j = 0; j < v; j ++)
{
int hh = 0, tt = -1;
for (int k = j; k <= m; k += v)
{
while (hh <= tt && k - q[hh] > s * v) hh ++;
while (hh <= tt && g[q[tt]] - q[tt] / v * w <= g[k] - k / v * w) tt --;
q[++ tt] = k;
f[k] = g[q[hh]] + (k - q[hh]) / v * w;
}
}
}
cout << f[m];
return 0;
}