NIKKEI Programming Contest 2019 F Jewels
学长讲的做法。
考虑将每种颜色的价值最大值和次大值取平均数,这样做显然不影响答案,并且好处是贪心时按双关键字排序只用考虑一种颜色当前只有一种物品的情况。
之后把所有物品扔进优先队列,每次从优先队列中取价值最大的物品。如果当前物品是第一次出现,就稍作调整即可。
具体而言,考虑有哪些决策。
-
可以不取当前这个物品,取还在队列里面且它的颜色之前已经出现超过两次的物品。取价值最大的即可。
-
如果要取当前物品,可以把之前拿过的相应颜色出现次数大于 \(2\) 的物品扔掉,再拿当前颜色的最大和次大。
-
也可以把前面只出现两次的物品扔掉,再往后面拿三个同色的(颜色前面没出现过),参考样例 \(1\)。
维护两个 multiset 即可,具体见代码。
code
#include <bits/stdc++.h>
using namespace std; typedef long long ll; typedef pair<ll, ll> pii; const int maxn = 200100;
ll n, m, s, a[maxn]; vector<ll> v[maxn];
int main() {
scanf("%lld%lld", &n, &m); for (int i = 0, x, y; i < n; ++i) scanf("%d%d", &x, &y), v[x].push_back(y * 2);
priority_queue<pii> pq; multiset<ll> S, T; ll X = 9e18, Y = 9e18;
for (int i = 1; i <= m; ++i) {
sort(v[i].begin(), v[i].end(), greater<ll>());
pq.emplace((v[i][0] + v[i][1]) / 2, i), pq.emplace((v[i][0] + v[i][1]) / 2, i);
for (int j = 2; j < v[i].size(); ++j) pq.emplace(v[i][j], i);
if (v[i].size() > 2) T.insert(v[i][0] + v[i][1] + v[i][2]);
}
while (pq.size()) {
ll x = pq.top().first, y = pq.top().second, ans = -2; pq.pop();
if (!a[y]) {
if (S.size()) ans = max(ans, s + *(--S.end()));
if (T.size()) ans = max(ans, s - Y + *(--T.end()));
ans = max(ans, s - X + x * 2);
if (v[y].size() > 2) T.erase(T.find(v[y][0] + v[y][1] + v[y][2]));
} else {
ans = s + x;
if (a[y] == 1) { for (int i = 2; i < (int)v[y].size(); ++i) S.insert(v[y][i]); Y = min(Y, v[y][0] + v[y][1]);
} else S.erase(S.find(x)), X = min(X, x);
}
s += x, ++a[y]; printf("%lld\n", ans / 2);
}
}

浙公网安备 33010602011771号