贪心与堆的优化

1.问题类型

如果题目中的问题涉及最多个数时,我们需要去观察是不是不管解的大小,
他对于最后Ans的值的大小的影响都是一样的,那么因为他的影响都是一样的,
我们就可以去寻找最优满足的条件,这里就可以用贪心来求满足情况。

2.堆优化

堆在这里的重要作用有两个一个是加速(logN),一个是方便存储和比较。
一个堆,我们把耗费最大/最小的放在堆顶,那么也就意味着,
我们可以通过堆来对它的价值进行自动排序,我们可以那当前的来比较。

3.后悔操作

后悔操作是这类题目中的重难点,
因为这类问题并不是绝对的贪心,我们需要通过不断的check能否进行后悔操作来保证得到的是最优解,在通过进堆和出堆来调整最优结构。

4.Example

T1 麦乐烤翅

Description
在太行山 路上, 有一家麦乐鸡翅的生意火爆。 因为好吃, 所以卖的特别好。 排队的人就特别 多, 经常有很多人买不到鸡翅。
鸡翅会在每分钟烤出 Xi 个, 每分钟也只会卖给一个客人, 第 i 个客人需要买 Yi 个。 因为生 意火爆, 老板可以选择在这分钟不卖给这个客人鸡翅, 或者卖给这个顾客他需要的鸡翅, 如 果现在剩余的鸡翅不够, 那就肯定不能卖给这个客人。 无论这个客人能否买到鸡翅, 他必须 离开队伍。
现在给定 N 分钟, 且已经知道每分钟烤出的鸡翅个数 Xi, 也知道每个客人需要鸡翅的 Yi 个 数, 现在老板想知道, 如何合理安排卖给与拒绝, 最多可以满足多少人
Input
第一行一个正整数 N, 表示有 N 分钟的时间卖鸡翅
第二行 N 个用空格隔开的整数 X1, X2……Xn, Xi 表示第 i 分钟会有 Xi 个鸡翅烤出
第三行 N 个用空格隔开的整数 Y1, Y2……Yn, Yi 表示第 i 分钟的顾客需要 Yi 个鸡翅
Output
一个整数, 表示最多可以满足买到鸡翅的人数。

#include<cstdio>
#include<queue>
#define LL long long
#define RG register
using namespace std;
const int N=250005;
inline LL read()
{
    RG LL x=0,w=1;RG char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*w;
}
LL n,Ans,now;
LL x[N],y[N];
priority_queue<int> Q;
int main()
{
    n=read();
    for(LL i=1;i<=n;i++)x[i]=read();
    for(LL i=1;i<=n;i++)y[i]=read();
    for(LL i=1;i<=n;i++)
    {
        now+=x[i];
        if(now>y[i])Ans++,Q.push(y[i]),now-=y[i];
        else if(!Q.empty()&&Q.top()>y[i])
        {
            now+=Q.top()-y[i];
            Q.pop();Q.push(y[i]);
        }
    }
    printf("%lld\n",Ans);
    return 0;
}

Solution

这就是一个非常典型的堆+贪心的操作;
能卖就卖,如果比能满足需求,我们就拿这个需要的鸡翅,
与最多需要的鸡翅做比较,如果小于那个鸡翅数量,
我们就可以把那个pop掉,把这个数据push进来。
因为不管你消耗的鸡翅数量是多少,最终对于答案的影响都只是+1;

T2 工作安排

Description
Farmer John 有太多的工作要做啊!!!!!!!!为了让农场高效运转,他必须靠他的工作赚钱,每项工作花一个单位时间。
他的工作日从0时刻开始,有1000000000个单位时间(!)。在任一时刻,他都可以选择编号1~N的N(1 <= N <= 100000)项工作中的任意一项工作来完成。
因为他在每个单位时间里只能做一个工作,而每项工作又有一个截止日期,所以他很难有时间完成所有N个工作,虽然还是有可能。
对于第i个工作,有一个截止时间D_i(1 <= D_i <= 1000000000),如果他可以完成这个工作,那么他可以获利P_i( 1<=P_i<=1000000000 ).
在给定的工作利润和截止时间下,FJ能够获得的利润最大为多少呢?答案可能会超过32位整型。
Input
第1行:一个整数N.
第2~N+1行:第i+1行有两个用空格分开的整数:D_i和P_i.
Output
输出一行,里面有一个整数,表示最大获利值。

Solution

这个题要求求工资的最大值,我们先按时间截止sort一下
如果当前日期即堆内元素多少小于截止日期就疯狂push
但当不行时,我们就可以比较大根堆(堆顶为最小元素)
我们就可以将堆内全部变为满足最大工资的东西
再将堆内全部pop出来Ans求和就可以了

#include<bits/stdc++.h>  
#define LL long long  
using namespace std;  
int n;  
struct Work{  
    LL date,w;  
    bool operator < (const Work a)const {  
        return date<a.date;  
    }  
}s[100000+5];  
priority_queue< LL,vector<LL>,greater<LL> > Q;    
int main(){
    cin>>n;
    LL ans=0;  
    for(int i=1;i<=n;i++)  
        cin>>s[i].date>>s[i].w; 
    sort(s+1,s+1+n);  
    for(int i=1;i<=n;i++){  
        if(Q.size()<s[i].date)Q.push(s[i].w);     
        else if(Q.top()<s[i].w){    
            Q.pop();   
            Q.push(s[i].w);  
        }  
    }  
    while(!Q.empty())ans+=Q.top(),Q.pop();  
    cout<<ans<<endl;
    return 0;
}  

5.实际运用需注意

有时候当给出的数据类型很多的时候我们需要用结构体入堆,
但cmp只用具有上述性质的影响因素

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值