1. 程式人生 > >BZOJ4977: [[Lydsy1708月賽]跳傘求生(不錯的貪心)

BZOJ4977: [[Lydsy1708月賽]跳傘求生(不錯的貪心)

4977: [[Lydsy1708月賽]跳傘求生

Time Limit: 5 Sec  Memory Limit: 256 MB
Submit: 446  Solved: 142
[Submit][Status][Discuss]

Description

小Q最近沉迷於《跳傘求生》遊戲。他組建了一支由n名玩家(包括他自己)組成的戰隊,編號依次為1到n。這個遊 戲中,每局遊戲開始時,所有玩家都會從飛機上跳傘,選擇一個目的地降落,跳傘和降落的時間有早有晚。在某局 遊戲降落前,他們在空中觀察發現地面上一共有m間房子,編號依次為1到m。其中每間房子恰好有一名敵人早於他 們到達。小Q戰隊的第i名玩家擁有a_i發子彈,地面上第i間房子裡的敵人擁有b_i發子彈,消滅他可以獲得c_i點積 分。每名玩家必須且只能選擇一間房子降落,然後去消滅裡面的敵人。若第i名玩家選擇了第j間房子,如果a_i>b_ j,那麼他就可以消滅該敵人,獲得a_i-b_j+c_j的團隊獎勵積分,否則他會被敵人消滅。為了防止團滅,小Q不允 許多名玩家選擇同一間房子,因此如果某位玩家毫無利用價值,你可以選擇讓他退出遊戲。因為房子之間的距離過 長,你可以認為每名玩家在降落之後不能再去消滅其它房間裡的敵人。作為小Q戰隊的指揮,請制定一套最優的降 落方案,使得最後獲得的團隊獎勵總積分最大

Input

第一行包含兩個正整數n,m(1<=n,m<=100000),分別表示戰隊的玩家數和地面上的房間數。 第二行包含n個正整數a_1,a_2,...,a_n(1<=a_i<=100000),分別表示每個玩家的子彈數。 接下來m行,每行兩個正整數b_i,c_i(1<=b_i,c_i<=100000),分別表示每個敵人的子彈數和獎勵積分。

Output

輸出一行一個整數,即最後獲得的團隊獎勵總積分的最大值。

Sample Input

3 3
4 4 4
2 3
1 3
5 3

Sample Output

11

HINT

Source

claris原創,本oj版權所有,翻版必究

 

思路:假設配對了P對,a序列肯定最大的P個。  我們對a從大到小排序,對敵人按照b從大到小按照。  假設當前的敵人b>=a,那麼現在這個敵人配對不了,但是他如果可以取替換前面的收益小的敵人,一定可以替換,因為他的b小於前面的b,一定能找到合適的a。  這部分用set維護就ok了。

關鍵是最後,我們刪去和為負的部分,因為這尾巴只會拉低收益。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define
ll long long #define F first #define S second using namespace std; const int maxn=100010; int a[maxn]; pair<int,int>b[maxn]; multiset<int>s1,s2; multiset<int>::iterator it; int main() { int N,M; ll ans=0; scanf("%d%d",&N,&M); rep(i,1,N) scanf("%d",&a[i]); rep(i,1,M) scanf("%d%d",&b[i].F,&b[i].S); sort(a+1,a+N+1); sort(b+1,b+M+1); int tail=M; for(int i=N;i>=1;i--){ while(b[tail].F>=a[i]){ int tmp=b[tail].S-b[tail].F; if(!s2.empty()&&*s2.begin()<tmp){ ans-=*s2.begin(); ans+=tmp; s2.erase(s2.begin()); s2.insert(tmp); } tail--; } if(!tail) break; ans+=b[tail].S-b[tail].F+a[i]; s1.insert(a[i]); s2.insert(b[tail].S-b[tail].F); tail--; } while(!s1.empty()){ if(*s1.begin()+*s2.begin()<0){ ans-=(*s1.begin()+*s2.begin()); s1.erase(s1.begin()); s2.erase(s2.begin()); } else break; } printf("%lld\n",ans); return 0; }