gdfzoj #791 硬币(优先队列)

来源:互联网 发布:小学同步教学软件 编辑:程序博客网 时间:2024/06/11 18:12

标签:优先队列
原题:AtCoder Grand Contest 018 C,gdfzoj上的链接
迟到的博客。。。

有X+Y+Z个人,从1到X+Y+Z编号。第i个人有Ai个金币,Bi个银币,Ci个铜币。
小J想从X个人手中得到他们的全部金币,Y个人手中得到他们的全部银币,Z个人手中得到他们的全部铜币。
一个人只会将他的一种硬币全部给小J。
小J想知道她最多能得到多少个硬币。

Input
第一行读入三个整数X,Y,Z
接下来X+Y+Z行,每行三个整数Ai,Bi,Ci
形如:
X Y Z
A1 B1 C1
A2 B2 C2
:
AX+Y+Z BX+Y+Z CX+Y+Z

Output
输出一行一个整数,表示小J最多能得到多少硬币。


我一开始想过用优先队列的方法,但是我想的是把数直接加进去,所以,GG。
以下讲正解。
考虑子问题a[i]=b[i]时的做法,将c[i]排序,取前c位,剩下的取a或b都行。
那么我们可以假(qin)设(ding)每个人都可以选c,然后按照(a[i]-b[i])排序。
显然序列存在一个位置使得该位置前全部选a[i]或b[i],该位置后全部选b[i]或c[i]。
枚举这个位置,用优先队列(注意最小的在最前面)直接加数,取一个max就能得出答案。

#include<cstdio>#include<algorithm>#include<cstring>#include<queue>#define maxn 100050using namespace std;typedef long long ll;struct smg{    int x,y,z;}a[maxn];bool cmp(smg sx,smg sy){    return sx.x-sx.y>sy.x-sy.y;}priority_queue<int,vector<int>,greater<int> >q;ll xx,yy,zz,i,n,l[maxn],r[maxn],ans=0ll;int main(){    scanf("%d%d%d",&xx,&yy,&zz); n=xx+yy+zz;    for (i=1;i<=n;i++) scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z);    sort(a+1,a+n+1,cmp);    for (i=1;i<=xx;i++) q.push(a[i].x-a[i].z),l[i]=l[i-1]+a[i].x;    for (i=xx+1;i<=n;i++)        if (a[i].x-a[i].z>q.top()) l[i]=l[i-1]-q.top()+a[i].x,q.pop(),q.push(a[i].x-a[i].z);        else l[i]=l[i-1]+a[i].z;    while (!q.empty()) q.pop();    for (i=n;i>=n-yy+1;i--) q.push(a[i].y-a[i].z),r[i]=r[i+1]+a[i].y;    for (i=n-yy;i>=1;i--)        if (a[i].y-a[i].z>q.top()) r[i]=r[i+1]-q.top()+a[i].y,q.pop(),q.push(a[i].y-a[i].z);        else r[i]=r[i+1]+a[i].z;    for (i=xx;i<=n-yy;i++) ans=max(ans,(ll)l[i]+r[i+1]);    printf("%lld",ans);     }
原创粉丝点击