Codeforces Round 935 (Div. 3)F(贪心)G(模拟)

CF1945F. Kirill and Mushrooms

题意:给n种蘑菇和一个排列p,每个蘑菇有一个魔力值,可以按任意顺序采集蘑菇,采集的价值为采集的蘑菇数量采集的蘑菇中的最小魔力值。如果采集k*个蘑菇,那么排列中 p 1 , p 2 , ⋯   , p k − 1 p_1,p_2, \cdots,p_{k-1} p1,p2,,pk1的蘑菇的魔力值都会变成0,你不会拿魔力值为0 的蘑菇,问最多能采集多少价值,且在最大价值的前提下找到采集蘑菇数量最小的一种方式。

思路:可以用对顶堆来做,一个大根堆维护将要采集的蘑菇,一个小根堆维护已经采集的蘑菇,每次采集蘑菇都看一下当前蘑菇魔力值会不会变成0即可,无论在哪一个堆的蘑菇的魔力值变成0,都将这个蘑菇弹出堆。之后从一开始遍历k,即可得到答案。



其实本质上就是将蘑菇的法力值从大到小排序,然后遍历每个蘑菇能不能取。

代码如下:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
#include <map>
#include <cmath>
#include <set>

using namespace std;

typedef long long ll;
typedef pair<ll, int> pii;

const int N = 1e5 + 10;

int main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    
    int T;cin>>T;
    while(T--){
        int n;cin>>n;
        vector<ll>a(n+1),p(n+1);
        for(int i=1;i<=n;i++)cin>>a[i];
        for(int i=1;i<=n;i++)cin>>p[i];

        multiset<ll,less<ll>>picked;
        multiset<ll,greater<ll>>to_pick;
        for(int i=1;i<=n;i++)to_pick.insert(a[i]);
        
        int k=1;
        pii ans={0,0};
        while(to_pick.size())
        {
            if(k>=2)
            {
                if(picked.find(a[p[k-1]])!=picked.end())picked.erase(picked.find(a[p[k-1]]));
                else to_pick.erase(to_pick.find(a[p[k-1]]));
            }
            if(k+k-1>n)break;//选的蘑菇和魔力变为0的蘑菇大于n就可以结束了

            while(picked.size()<k)//此时采集的蘑菇不足k个,要从待采集的蘑菇中选取
            {
                picked.insert(*to_pick.begin());
                to_pick.erase(to_pick.begin());
            }
            
            ll num=k*(*picked.begin());
            if(ans.first==num){
                ans.second=min(ans.second,k);
            }else if(ans.first<num){
                ans={num,k};
            }

            k++;
        }
        cout<<ans.first<<" "<<ans.second<<"\n";
    }
    return 0;
}

CF1945G. Cook and Porridge
题意n名学生准备喝粥,有一个厨师将在D分钟内给他们打粥,每一分钟为一名同学打粥,每名同学都有一个吃粥时间s,如果第i个学生在x分钟开始时吃到一份粥,那么他将在第 x + s i x+s_i x+si分钟结束时回到队列中排队,同时每名学生都有一个优先值,当他回到队列的时候,他会站到第一个大于等于他的优先值的同学后面,也就是他会插队,如果有几个同学同时返回队列,他们会按 s i s_i si的升序返回队列。问在D分钟内,所有的学生是否至少都能吃一次粥。

思路:因为每分钟最多只给一个同学打粥,因此按题意模拟即可,用一个优先队列维护插队的同学,在用一个vector记录下来每个时刻插队的人的编号,但是这里插队也是有优先级的,即:
1、优先级高的同学排在前面
2、回到队列的时间靠前的同学排在前面
3、吃粥时间 s i s_i si小的同学排在前面
优先级大小为 1 > 2 > 3 1>2>3 1>2>3

学到了用优先队列可以插入array数组,改变数组元素的正负号即可同时按大或小排序
接着按题意模拟即可:有一些小技巧,具体看代码

#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
#include <map>
#include <cmath>
#include <set>
#include <iomanip>
#include <numeric>
#include <array>
#include <queue>

using namespace std;

typedef long long ll;
typedef pair<int, int> pii;

const int N = 1e5 + 10;
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
#include <map>
#include <cmath>
#include <set>
#include <iomanip>
#include <numeric>
#include <array>
#include <queue>

using namespace std;

typedef long long ll;
typedef pair<int, int> pii;

const int N = 1e5 + 10;

int main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    
    int T;cin>>T;
    while(T--)
    {
        int n,D;cin>>n>>D;
        vector<ll>k(n+2),s(n+2);
        for(int i=1;i<=n;i++)cin>>k[i]>>s[i];

        auto suf=k;
        for(int i=n;i>=1;i--)suf[i]=max(suf[i],suf[i+1]);

        priority_queue<array<int,4>>q;//插队队列
        vector<vector<int>>pos(D+1);//该时间插入的人
        int cur=1;//当前第一个没吃饭的人
        int ans=-1;

        for(int i=1;i<=D;i++){
            if(!q.empty()&&q.top()[0]>suf[cur]){//如果能插队
                auto [p,t,s,id]=q.top();q.pop();
                s=-s;
                if(i+s<=D){
                    pos[i+s].push_back(id);
                }
            }
            else{//如果没人插队,那么就可以顺延一个人吃粥
                if(i+s[cur]<=D){
                    pos[i+s[cur]].push_back(cur);
                }
                cur++;
            }
            if(cur==n+1){//说明每个人都至少吃过一次粥了
                ans=i;
                break;
            }
            for(auto id:pos[i]){
                array<int,4>b={k[id],-i,-s[id],id};
                q.push(b);//这里有个小细节,因为是大根堆,回到队列的时间和吃粥的时间用负数,就可以保证是从小到大排序
            }
        }
        cout<<ans<<"\n";
    }
    return 0;
}
···

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值