Codeforces 1140C Playlist 題解

題意:

    給你一個n,給你一個k,表示從n個二元組 <x, y> 中最多選出k個,產生一個貢獻值。這個貢獻值指的是,這選出的最多k個二元組的x屬性加和,乘這些選出的二元組的y屬性的最小值。求這個貢獻值的最大值。

思路:

    我的想法是,結構體排序加優先隊列維護隊頭處理,結構體本身按照y屬性升序排,然後優先隊列按照x屬性降序,即隊頭是最小的大體是爲了讓在x屬性降序以後,我每次維護一個長度爲k的優先隊列(從後往前篩),如果找到一個比當前隊頭大的,那就維護一下貢獻的最大值;如果比當前隊頭要小,那就相當於當前維護到的這個值的x屬性和y屬性都比當前隊頭要小(因爲y升序排的),大體思路就是如此,還有一些細節會在下面詳述。(當然也可以使用pair類型的優先隊列)具體思路如下:

1)先定義結構體,然後按照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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章