P3045 [USACO12FEB]牛券Cow Coupons

题目入口


FJ准备买一些新奶牛,市场上有N头奶牛(1<=N<=50000),第i头奶牛价格为Pi(1<=Pi<=10^9)。

FJ有K张优惠券,使用优惠券购买第i头奶牛时价格会降为Ci(1<=Ci<=Pi),每头奶牛只能使用一次优惠券。

FJ想知道花不超过M(1<=M<=10^14)的钱最多可以买多少奶牛?


这是一道使用堆优化的堆题。

我们肯定贪心的想,每次买最小价值的牛。

可是由于优惠劵这种无良商家的优惠政策,导致我们没有办法直接贪心。

我们先将所有牛的所有状态全部弄到一个小根堆里。

然后依次取出,如果当前的牛的价格是需要使用优惠劵,而且我们有充足的优惠劵。我们肯定使用。

如果优惠劵不够了。我们就要考虑反悔操作,就是从已经使用的优惠劵中选出一张来。看看用在这头牛上面是否更优。

这就是我们的大体贪心策略。

那我们如何确定我们要从已使用忧患卷的牛中,选出一个将牛A的优惠劵拿掉呢?我们就需要考虑A的优惠价和原价的差加上现在堆顶的牛的价格是否小于堆顶的牛的原价。

如果小于,则说明在牛B上使用优惠劵更加优秀。。

否则就是牛A更加优秀。

然后根据上面的判断决定是否要买。

然后我们如何选出已优惠券使用的牛的原价和优惠价最小呢?

答案还是使用堆维护。

然后是冗长的代码

#include<cstdio>
#include<algorithm>
#include<iostream>
const int maxn=101000;
using std::swap;
struct Data
{
    long long val;
    int base;
    int pos;
    bool operator <(const Data &a)const 
    {
        return val<a.val;
    }
};//数据类型
struct Heap
{
    Data data[maxn];
    int len;
    Data top()
    {
        return data[1];
    }
    void push(Data a)
    {
        data[++len]=a;
        int pos=len;
        while(pos&&data[pos]<data[pos>>1])
        {
            swap(data[pos],data[pos>>1]);
            pos>>=1;
        }
    }
    void del()
    {
        swap(data[1],data[len--]);
        int now=1,pos;
        while(1)
        {
            pos=now;
            if(data[now<<1]<data[pos]&&(now<<1)<=len)
                pos=now<<1;
            if(data[now<<1|1]<data[pos]&&(now<<1|1)<=len)
                pos=now<<1|1;
            if(now==pos)    break;
            swap(data[now],data[pos]);
            now=pos;
        }
    }
};//堆模板
Heap h1,h2;//h1为储存所有的优惠价和原价的堆,h2为原价和优惠价的差的堆
long long cow[maxn][2];//第二维0为原价,1位优惠价
bool vis[maxn];//记录某个牛是否被用过
int main()
{
    int n,k;
    long long m;
    scanf("%d%d%lld",&n,&k,&m);
    long long a;
    for(int i=1;i<=n;i++)
    {
        Data pas;//入堆,我知道很冗长,将就的看吧
        pas.pos=i;
        scanf("%lld",&a);
        cow[i][0]=a;
        pas.base=0; pas.val=a;
        h1.push(pas);
        scanf("%lld",&a);
        cow[i][1]=a;
        pas.base=1; pas.val=a;
        h1.push(pas);
    }
    int copy_k=k;
    int ans=0;
    while(copy_k)//现将优惠劵全用完
    {
        Data pas=h1.top();
        h1.del();
        if(vis[pas.pos])    continue;//如果一个牛已经被购买,那我们就跳过
        if(pas.base)//当前价格使用了优惠劵
        {
            copy_k--;//个数减一
            Data nxt;
            nxt.val=cow[pas.pos][0]-cow[pas.pos][1];//处理出他的原价和优惠价的差并入堆
            h2.push(nxt);
        }
        if(m>pas.val)
            m-=pas.val;//可以继续购买
        else
        {
            printf("%d",ans);
            return 0;//直接输出答案
        }
        vis[pas.pos]=true;
        ans+=1;
    }
    while(h1.len)//很迷的判断
    {
        Data pas=h1.top();//一样的贪心
        h1.del();
        if(vis[pas.pos])    continue;//如果已购买
        if(m<pas.val)   break;//不能继续购买
        if(pas.base)//使用了优惠劵
        {
            Data b=h2.top();//拿出差的最小值比较
            if(b.val+pas.val<cow[pas.pos][0])//满足反悔条件
            {//反悔,并将新的牛的原价个优惠价的差入到h2里
                h2.del();
                m-=(b.val+pas.val);
                b.val=cow[pas.pos][0]-cow[pas.pos][1];
                h2.push(b);
                vis[pas.pos]=true;
        }
                ans+=1;
            }
        else
        {
            m-=pas.val;
            vis[pas.pos]=true;
            ans+=1;//不使用优惠劵,其实这一句应该不用加,是我的一个同学告诉我好像数据里有优惠价比原价贵,然后就加了这么一句
        }
    }
    printf("%d",ans);
    //输出答案
}

猜你喜欢

转载自www.cnblogs.com/Lance1ot/p/9294650.html