Loading

【题解】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;
}
posted @ 2023-01-04 21:40  kymru  阅读(33)  评论(0编辑  收藏  举报