题意:
给你一个n,给你一个k,表示从n个二元组 <x, y> 中最多选出k个,产生一个贡献值。这个贡献值指的是,这选出的最多k个二元组的x属性加和,乘这些选出的二元组的y属性的最小值。求这个贡献值的最大值。
思路:
我的想法是,结构体排序加优先队列维护队头处理,结构体本身按照y属性升序排,然后优先队列按照x属性降序,即队头的x是最小的 ,为了让在x属性降序以后,我每次维护一个长度为k的优先队列(从后往前筛),如果找到一个比当前队头大的,那就维护一下贡献的最大值;如果比当前队头要小,那就相当于当前维护到的这个值的x属性和y属性都比当前队头要小(因为y升序排的),大体思路就是如此,还有一些细节会在下面详述。(当然也可以使用pair类型的优先队列)具体思路如下:
1)先定义结构体,在内部重载,即定义优先队列的排序方式,让队头的x一直是最小的,然后按y升序排。
2)因为是最多选k个,那么我先按k个选,那么我就先初始化一个长度为k的优先队列,那就将按照y升序排列后的结构体的后k项压进优先队列,然后按照开头我说的方式(黑色加粗下划线部分)维护,就能处理处当前情况下res的一个最大值了。
3)不过因为最多选k个,不是正好选k个,那么上述维护方式就有所欠缺,下面分析一下欠缺的地方。
i)先考虑第一次初始化的优先队列的后面:
我第一次先初始化一个长度为k的优先队列,那么之后的按照上述维护方式维护过程中,会不会出现长度不为k但是贡献还比当前维护的res大的呢,答案显然是不会的,因为在当前这个最小的队头一定时,我肯定是乘的x越多我产生的贡献就越大,所以我肯定要让我一直维护的区间长度达到最大的k。
ii)再考虑第一次是初始化的优先队列内部有没有更大的贡献值:
答案是不一定的,那么我先看这段最初的优先队列中,每一项能产生的各自的贡献值是多大,每次维护res。接下来我看选了多于1个但是不足k个的情况,答案就是一直不断压进优先队列维护就好了,这个想法和之前的一致,队头一直在维护一个最小的,那么我肯定让x加的越多越好。
这样本题的分析就结束了,下面是AC代码。
我的AC代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxx = 3e5 + 7;
const int Inf = 1 << 30;
const ll INF = 1ll << 60;
int n, k;
struct PP {
ll x, y;
friend bool operator < (PP u, PP v) {
return u.x > v.x;
}
} p[maxx];
priority_queue <PP> qua;
bool cmp(PP a, PP b) { return a.y < b.y; }
int main() {
ll res = 0;
scanf("%d %d", &n, &k);
for(int i = 1; i <= n; i++) {
scanf("%I64d %I64d", &p[i].x, &p[i].y);
res = max(res, p[i].x * p[i].y);
}
sort(p + 1, p + n + 1, cmp);
ll ans = 0;
for(int i = n; i >= n - k + 1; i--) {
qua.push(p[i]);
ans += p[i].x;
}
res = ans * p[n - k + 1].y;
for(int i = n - k; i >= 1; i--) {
PP top = qua.top();
if(p[i].x > top.x) {
ans -= top.x;
ans += p[i].x;
qua.pop();
qua.push(p[i]);
res = max(res, ans * p[i].y);
}
}
while(!qua.empty()) qua.pop();
ans = 0;
for(int i = n; i >= n - k + 2; i--) {
qua.push(p[i]);
ans += p[i].x;
res = max(res, ans * p[i].y);
}
printf("%I64d\n", res);
return 0;
}