gdfzoj #791 硬币(优先队列)

标签:优先队列
原题: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
#include
#include
#include
#define maxn 100050
using 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);     
}

你可能感兴趣的:(优先队列)