题目链接:宝物筛选
题目描述
终于,破解了千年的难题。小 找到了王室的宝物室,里面堆满了无数价值连城的宝物。
这下小 可发财了,嘎嘎。但是这里的宝物实在是太多了,小 的采集车似乎装不下那么多宝物。看来小 FF 只能含泪舍弃其中的一部分宝物了。
小 对洞穴里的宝物进行了整理,他发现每样宝物都有一件或者多件。他粗略估算了下每样宝物的价值,之后开始了宝物筛选工作:小 有一个最大载重为 的采集车,洞穴里总共有 nn 种宝物,每种宝物的价值为 ,重量为 ,每种宝物有 件。小 希望在采集车不超载的前提下,选择一些宝物装进采集车,使得它们的价值和最大。
输入格式
第一行为一个整数 和 ,分别表示宝物种数和采集车的最大载重。
接下来 行每行三个整数 。
输出格式
输出仅一个整数,表示在采集车不超载的情况下收集的宝物的最大价值。
输入输出样例
输入
4 20
3 9 3
5 9 1
9 4 2
8 1 3
输出
47
说明/提示
对于 的数据,,。
对于 的数据,,,。
思路
这题没什么好说的,一道典型的多重背包题目,就是数据太大了,要是套模板的话肯定会爆,所以就需要用到二进制优化或单调队列优化。
二进制优化
二进制优化就是把数量依次分解为若干个二进制数和剩下的余数,而这些数可以组合表示1~m中的任意一个整数,通过这样的方式,等于把我们的物品的总数变多了,然而每个物品的数量都是1,最后动态规划的时候就相当于做01背包了
时间上,原本是数量从1到m进行枚举,优化后变成了从2^0乘2枚举,时间大大缩短了。那么就直接上代码。
代码
/*************************************************
Note:二进制优化
*************************************************/
#include <queue>
#include <stack>
#include <stdio.h>
#include <iostream>
#include <vector>
#include <iomanip>
#include <string.h>
#include <algorithm>
#include <cmath>
#include <cstring>
#define ll long long
#define ull unsigned long long
using namespace std;
const int N = 1e5 + 10;
const int INF = 0x3f3f3f3f;
inline int read()
{
int s = 0, w = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
w = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
s = s * 10 + ch - '0', ch = getchar();
return s * w;
}
int n, m, ans, cnt = 1;
int f[N];
int w[N], v[N];
int main()
{
int a, b, c;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i)
{
scanf("%d%d%d", &a, &b, &c);
for (int j = 1; j <= c; j <<= 1)
{
v[++cnt] = j * a, w[cnt] = j * b;
c -= j;
}
if (c)
{
v[++cnt] = a * c, w[cnt] = b * c;
}
}
for (int i = 1; i <= cnt; ++i)
{
for (int j = m; j >= w[i]; --j)
{
f[j] = max(f[j], f[j - w[i]] + v[i]);
}
}
printf("%d\n", f[m]);
return 0;
}
到这里这道题二进制优化就解决了,但是还有一种单调队列优化的思路我也讲一下。
单调队列优化
我们知道状态转移方程:
f[i][j]=max(f[i−1][j−w∗k]+v∗k);(k<=c)
现在我们要把这个方程变成一个单调队列可以优化的形式,于是我们假设:d=j mod w[i],s=⌊jw[i]⌋
f[i][j]=max(f[i−1][d+w∗k]−v∗k)+v∗s(s-k<=c)
之后我们枚举余数d,然后对于每个余数d都用单调队列优化即可。上代码!
代码
/*************************************************
Note:单调队列优化
*************************************************/
#include <queue>
#include <stack>
#include <stdio.h>
#include <iostream>
#include <vector>
#include <iomanip>
#include <string.h>
#include <algorithm>
#include <cmath>
#include <cstring>
#define ll long long
#define ull unsigned long long
using namespace std;
const int N = 1e6 + 10;
const int INF = 0x3f3f3f3f;
inline int read()
{
int s = 0, w = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-')
w = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
s = s * 10 + ch - '0', ch = getchar();
return s * w;
}
int n, V, ans, head, tail;
int q[N], q2[N], dp[N];
int main()
{
scanf("%d%d", &n, &V);
for (int i = 1; i <= n; i++)
{
int v, w, c;
scanf("%d%d%d", &w, &v, &c);
if (v == 0)
{
ans += w * c;
continue;
}
int k = V / v;
c = min(c, k);
for (int d = 0; d < v; d++)
{
head = tail = 0;
k = (V - d) / v;
for (int j = 0; j <= k; j++)
{
while (head < tail && dp[d + j * v] - j * w >= q2[tail - 1])
tail--;
q[tail] = j;
q2[tail++] = dp[d + j * v] - j * w;
while (head < tail && q[head] < j - c)
++head;
dp[d + j * v] = max(dp[d + j * v], q2[head] + j * w);
}
}
}
printf("%d", ans + dp[V]);
return 0;
}