• JZOJ 5461 购物 —— 贪心


    题目:https://jzoj.net/senior/#main/show/5461

    贪心,原来想了个思路,优先选优惠价最小的 K 个,然后其他按原价排序遍历;

    如果当前物品没选过,原价选上,如果选过,考虑把它换成原价,然后把优惠价最小的下一个选上;

    但这样做是75分,没考虑 替换没选过的物品 和 比较替换后是否更优;

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    using namespace std;
    typedef long long ll;
    int const maxn=5e4+5;
    int n,K,cnt;
    ll m,ans;
    bool vis[maxn];
    struct N{
        int w,t,id;
        bool operator < (const N &y) const
            {return w>y.w;}
    }p[maxn];
    priority_queue<N>q;
    bool cmp(N x,N y){return x.t<y.t;}
    int main()
    {
        freopen("shopping.in","r",stdin);
        freopen("shopping.out","w",stdout);
        scanf("%d%d%lld",&n,&K,&m);
        for(int i=1;i<=n;i++)scanf("%d%d",&p[i].w,&p[i].t);
        sort(p+1,p+n+1,cmp);
        for(int i=1;i<=n;i++)p[i].id=i,q.push(p[i]);
        for(int i=1;i<=K&&i<=n;i++)
        {    
            if(ans+p[i].t>m)break;
            vis[i]=1; cnt=i; ans+=p[i].t;
        }
        if(cnt==n){printf("%d
    ",n); return 0;}
        while(q.size())
        {
            int x=q.top().id,w=q.top().w,t=q.top().t; q.pop();
            if(!vis[x])
            {
                if(ans+w>m)continue;
                ans+=w; cnt++; 
            }
            else
            {
                if(ans-p[x].t+p[x].w+p[K+1].t>m)continue;
                ans=ans-p[x].t+p[x].w+p[K+1].t; K++; cnt++;
                vis[x]=0; vis[K]=1;
            }
        }
        printf("%d
    ",cnt);
        return 0;
    }

    正解是直接把选中物品的 原价 - 优惠价 放入小根堆,然后其他物品按原价排序,直接判断、替换;

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    using namespace std;
    typedef long long ll;
    int const maxn=5e4+5;
    int n,K,cnt;
    ll m,ans;
    bool vis[maxn];
    struct N{int w,t,id;}p[maxn];
    priority_queue<int>q;
    bool cmp(N x,N y){return x.t<y.t;}
    bool cmp2(N x,N y){return x.w<y.w;}
    int main()
    {
    //    freopen("shopping.in","r",stdin);
    //    freopen("shopping.out","w",stdout);
        scanf("%d%d%lld",&n,&K,&m);
        for(int i=1;i<=n;i++)scanf("%d%d",&p[i].w,&p[i].t),p[i].id=i;
        sort(p+1,p+n+1,cmp);
        for(int i=1;i<=K&&i<=n;i++)
        {    
            if(ans+p[i].t>m)break;
            cnt=i; ans+=p[i].t; 
            q.push(p[i].t-p[i].w); vis[p[i].id]=1;
        }
        if(cnt==n){printf("%d
    ",n); return 0;}
        sort(p+1,p+n+1,cmp2);
        for(int i=1,x;i<=n;i++)
        {
            if(vis[p[i].id])continue;
            if(q.size())
            {
                x=-q.top();
                if(x+p[i].t>p[i].w&&ans+p[i].w<=m)ans+=p[i].w,cnt++;
                else if(x+p[i].t<=p[i].w&&ans+x+p[i].t<=m)ans+=x+p[i].t,cnt++,q.pop(),q.push(p[i].t-p[i].w);
            }
            else if(ans+p[i].w<=m)ans+=p[i].w,cnt++;
        } 
        printf("%d
    ",cnt);
        return 0;
    }
    TJ

    但这样总感觉不对,因为按原价排序并不能保证替换最优;

    这里就是反例:

    6 3 15

    10 3

    8 4

    7 5

    5 1

    4 2

    3 2

    按这样的做法,会先选后3个物品,然后按 1,2,3 把前三个物品排序;

    然后把物品6换成原价购买,优惠价购买物品 3;

    之后就不能买了,输出4;

    但实际上应该是优惠价购买物品 1,2,4,原价购买物品 5,6,答案是5;

     所以应该采用别的贪心策略,看到了一种很好的:https://blog.csdn.net/qq_40448823/article/details/81488195 (不过这篇博客贴错题面了囧)

    所有物品都按优惠价排序,同时开了一个原价购买的大根堆,存已经原价买下的东西的原价;

    先买 K 个优惠价的,然后从优惠价排序的顺序继续往后看,每次去掉优惠买中 原价 - 优惠价 最小的一个,优惠价买下一个;

    然后回头看看能否原价买上去掉的这个东西,能就原价买上,不能就去原价物品堆里看看,如果能替换一下使花钱更少,那么就替换一下;

    而如果优惠价购买下一个不如原价购买下一个优,那么原价购买下一个,同样进行替换的判断;

    然后就能过掉上面的数据了,主要是因为优惠价部分排序满足,原价部分用大根堆替换来满足;

    不过数据太水,也不知道这个做法是否完美无瑕,看样子应该没问题,先放到这里吧。

    代码如下:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    using namespace std;
    typedef long long ll;
    int const maxn=50005;
    int n,K,ans;
    ll sum,m;
    struct N{
        int w,t,c;
        bool operator < (const N &y) const
            {return c>y.c;}
    }p[maxn];
    priority_queue<N>Q;
    priority_queue<int>q;
    bool cmp(N x,N y){return x.t<y.t;}
    int main()
    {
        freopen("shopping.in","r",stdin);
        freopen("shopping.out","w",stdout);
        scanf("%d%d%lld",&n,&K,&m);
        for(int i=1;i<=n;i++)scanf("%d%d",&p[i].w,&p[i].t),p[i].c=p[i].w-p[i].t;
        sort(p+1,p+n+1,cmp);
        for(int i=1;i<=n;i++)
        {
            if(Q.size()<K&&sum+p[i].t<=m){sum+=p[i].t; ans++; Q.push(p[i]);}//Q是按差价排序的堆 
            else if(Q.size()==K)//其他物品是按优惠价排序的 
            {
                N x=Q.top();
                if(x.c+p[i].t<=p[i].w)//优惠价购买较优 
                {
                    Q.pop(); Q.push(p[i]); 
                    sum=sum-x.t+p[i].t;//先优惠价买上 
                    if(sum+x.w<=m){sum+=x.w; ans++; q.push(x.w);}//可以原价买原来那个 //q是原价购买了的堆 
                    else if(q.size()&&q.top()>x.w){sum=sum-q.top()+x.w; q.pop(); q.push(x.w);}
                    //不能买了,换掉之前原价购买的一个物品,可以更优
                }
                else if(sum+p[i].w<=m){sum+=p[i].w; ans++; q.push(p[i].w);}//原价购买 
                else if(q.size()&&q.top()>p[i].w){sum=sum-q.top()+x.w; q.pop(); q.push(x.w);}//不能买了,替换更优 
            }
        }
        printf("%d
    ",ans);
        return 0;
    }
  • 相关阅读:
    MagicZoom bug-Strict Standards: Only variables should be assigned by reference Error
    Web大文件(夹)上传(断点续传)控件-Xproer.HttpUploader6
    在PHP中,通过filesize函数可以取得文件的大小,文件大小是以字节数表示的。如果要转换文件大小的单位,可以自己定义函数来实现。
    PHP正则匹配6到16位字符组合(且只能为数字、字母、下划线)
    Windows下PHP版本选取
    简单配置nginx使之支持pathinfo
    PHP如何关闭notice级别的错误提示
    php开启pathinfo 模式
    php 5.3新增的闭包语法介绍function() use() {}
    Object.prototype.toString.call() 区分对象类型
  • 原文地址:https://www.cnblogs.com/Zinn/p/9439885.html
Copyright © 2020-2023  润新知