题目地址:
https://www.luogu.com.cn/problem/P1776
题目描述:
终于,破解了千年的难题。小FF找到了王室的宝物室,里面堆满了无数价值连城的宝物。这下小FF可发财了,嘎嘎。但是这里的宝物实在是太多了,小FF的采集车似乎装不下那么多宝物。看来小FF只能含泪舍弃其中的一部分宝物了。
小FF对洞穴里的宝物进行了整理,他发现每样宝物都有一件或者多件。他粗略估算了下每样宝物的价值,之后开始了宝物筛选工作:小FF有一个最大载重为
W
W
W的采集车,洞穴里总共有
n
n
n种宝物,每种宝物的价值为
v
i
v_i
vi,重量为
w
i
w_i
wi,每种宝物有
m
i
m_i
mi件。小FF希望在采集车不超载的前提下,选择一些宝物装进采集车,使得它们的价值和最大。
输入格式:
第一行为一个整数
n
n
n和
W
W
W,分别表示宝物种数和采集车的最大载重。
接下来
n
n
n行每行三个整数
v
i
,
w
i
,
m
i
v_i,w_i,m_i
vi,wi,mi。
输出格式:
输出仅一个整数,表示在采集车不超载的情况下收集的宝物的最大价值。
数据范围:
对于
30
%
30\%
30%的数据,
n
≤
∑
m
i
≤
1
0
4
n\leq \sum m_i\leq 10^4
n≤∑mi≤104,
0
≤
W
≤
1
0
3
0\le W\leq 10^3
0≤W≤103。
对于
100
%
100\%
100%的数据,
n
≤
∑
m
i
≤
1
0
5
n\leq \sum m_i \leq 10^5
n≤∑mi≤105,
0
≤
W
≤
4
×
1
0
4
0\le W\leq 4\times 10^4
0≤W≤4×104,
1
≤
n
≤
100
1\leq n\le 100
1≤n≤100。
多重背包问题,可以用单调队列优化来做,参考https://blog.csdn.net/qq_46105170/article/details/114256956。代码如下:
#include <iostream>
using namespace std;
const int N = 110, M = 4e4 + 10;
int n, m;
int v[N], w[N], s[N];
int f[N][M];
int q[M];
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%d%d%d", &w[i], &v[i], &s[i]);
for (int i = 1; i <= n; i++)
for (int j = 0; j < v[i]; j++) {
int hh = 0, tt = 0;
for (int k = 0; k <= (m - j) / v[i]; k++) {
int l = j + k * v[i];
if (hh < tt && v[i] * s[i] < l - q[hh]) hh++;
while (hh < tt && f[i - 1][q[tt - 1]] - q[tt - 1] / v[i] * w[i] <=
f[i - 1][l] - k * w[i])
tt--;
q[tt++] = l;
f[i][l] = f[i - 1][q[hh]] + (l - q[hh]) / v[i] * w[i];
}
}
printf("%d\n", f[n][m]);
}
时空复杂度 O ( n W ) O(nW) O(nW)。