牛客 tokitsukaze and Soldier
题意
有 n n n 个士兵,每个士兵有战斗力 v v v ,选了这个士兵之后要求最多选 s s s 个士兵,求如何选士兵,使得士兵的战斗力总和最大。
题解
可以先把士兵按照 s s s 从大到小排序,那么对于可选择的士兵数量的限制就是选择的最右边的 s s s ,并且 s s s 递减,那么先选择那个士兵,然后可以在左边选战斗力最大的 s − 1 s-1 s−1 个士兵。
使用优先队列(小根堆)维护即可,如果当前优先队列里的士兵数量大于 s s s ,则要pop战斗力最小的士兵。
代码
#pragma region
//#pragma optimize("Ofast")
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <vector>
using namespace std;
typedef long long ll;
#define tr t[root]
#define lson t[root << 1]
#define rson t[root << 1 | 1]
#define rep(i, a, n) for (int i = a; i <= n; ++i)
#define per(i, a, n) for (int i = n; i >= a; --i)
#pragma endregion
const int maxn = 1e5 + 5;
struct node {
int v, s;
bool operator<(const node &A) const { return v > A.v; }
} a[maxn];
int main() {
int n;
scanf("%d", &n);
rep(i, 1, n) scanf("%d%d", &a[i].v, &a[i].s);
sort(a + 1, a + 1 + n, [](const node &A, const node &B) {
return A.s > B.s;
});
priority_queue<node> q;
ll ans = 0, tmp = 0;
for (int l = 1, r = 1; l <= n; l = r + 1, r = l) {
while (r + 1 <= n && a[r].s == a[r + 1].s) ++r;
rep(i, l, r) q.push(a[i]), tmp += a[i].v;
while (q.size() > a[r].s) tmp -= q.top().v, q.pop();
ans = max(ans, tmp);
}
printf("%lld\n", ans);
}