【题解】CF808E Selling Souvenirs
题意
多重背包,但数据范围很大并且体积小于等于三。
思路
乱搞。
很自然地考虑将物品按照体积分成三类。
显然对于同一类的物品从最大开始取最优,那么有一个贪心的想法。
直接枚举其中两类物品的数量,可以得到一个流浪者暴力。
令 \(f(a, b)\) 为取 \(a\) 个第一类物品,\(b\) 个第二类物品的最大收益,那么 \(f\) 是关于 \(a\) 单峰的。
那么只需要枚举第三类物品的数量,然后三分求最大值即可。
注意可能会有平台,随便搞一下。
代码
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 5;
int n, m;
int w[maxn], c[maxn];
vector<ll> ws[4];
bool cmp(int a, int b) { return (a > b); }
ll calc(ll x, ll y)
{
//printf("%d %d\n",x,y/2);
if (y % 2) x++;
if (ws[1].size() == 0 || x == 0) x = 0;
else x = ws[1][min(x - 1, (ll)ws[1].size() - 1)];
if (ws[2].size() == 0 || y < 2) y = 0;
else y = ws[2][min(y / 2 - 1, (ll)ws[2].size() - 1)];
return x + y;
}
int main()
{
ll ans = 0;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
{
scanf("%d%d", &c[i], &w[i]);
ws[c[i]].push_back(w[i]);
}
for (int i = 1; i <= 3; i++)
{
sort(ws[i].begin(), ws[i].end(), cmp);
for (int j = 1; j < ws[i].size(); j++) ws[i][j] += ws[i][j - 1];
}
for (int i = 0; i <= min(m / 3, (int)ws[3].size()); i++)
{
int rem = m - 3 * i;
int l = 0, r = min(rem, (int)ws[1].size());
if (ws[1].size() + 2 * ws[2].size() <= rem)
{
ans = max(ans, (i ? ws[3][i - 1] : 0) + calc(ws[1].size(), 2 * ws[2].size()));
continue;
}
while (l < r)
{
int mid1 = l + (r - l) / 3, mid2 = r - (r - l) / 3;
ll val1 = calc(mid1, rem - mid1), val2 = calc(mid2, rem - mid2);
// printf("debug %d %d\n", val1, val2);
if (val1 > val2) r = mid2 - 1;
else l = mid1 + 1;
}
// printf("debug %d\n", l);
ans = max(ans, (i ? ws[3][i - 1] : 0) + calc(l, rem - l));
}
printf("%lld\n", ans);
return 0;
}