題意:
給你一個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; }