Codeforces 1140C Playlist 题解

题意:

    给你一个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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值