题解 P2859 【[USACO06FEB]摊位预订Stall Reservations】

  • 题目链接:

    https://www.luogu.org/problemnew/show/P2859

  • 思路:

    • 首先大家会想到这是典型的贪心,类似区间覆盖问题的思路,我们要将每段时间的左端点从小到大排序,然后一个个插入,插入时比较是否先前的牛棚中已经有牛挤完了奶,如果没有就新增一个牛棚,否则用挤完奶的牛棚。

    • 如果插入时扫描一遍找可用的牛棚的话肯定是会超时的,那么我们就要用一个priority_queue(当然你手写堆也可以)维护先前插入的牛棚中最早挤完奶,即右端点最小的那个。

  • Tips:

    • 注意排序后奶牛的顺序可能是混乱的,而输出是要按原来输入的顺序。我们可以用一个index记录每头奶牛先前的数组下标,以便在记录摊位位置时不会出错。

    • 如何判断每头奶牛在哪个摊位???

      • 虽然感觉第一篇题解的二分+树状数组好神奇但是我看不懂
      • 感觉第三篇的差分好神奇但我还是看不懂

      于是就用了土方法

      我们用一个be数组记录每个奶牛对应的摊位,如果前面没有可插入的奶牛,则新增一个。如果前面有一个奶牛挤完了奶,那么我们就用它的牛棚,具体请看代码实现。

  • 代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cctype>
#include <vector>
#include <queue>
using namespace std;
const int maxn=50005;
int n,be[maxn],cnt=0;
struct Cow{
    int s,e,index;
}cow[maxn];
struct Num{    //优先队列记录下标
    int x;
    bool operator < (const Num &a) const{
    return cow[x].e>cow[a.x].e;
    }
};
inline bool cmp1(const Cow &a,const Cow &b){
    return a.s<b.s;
}
priority_queue< Num,vector<Num>>poi;
int main()
{
    cin>>n;
    for(register int i=1;i<=n;i++){
        scanf("%d %d",&cow[i].s,&cow[i].e);
        cow[i].index=i;
    }
    sort(cow+1,cow+1+n,cmp1);
    for(register int i=1;i<=n;i++){
        Num k;k.x=i;
        if(!poi.empty()){  
            Num p=poi.top();//取出队首时间最短的
            if(cow[p.x].e>=cow[i].s){
                cnt++;
                poi.push(k);
                be[cow[i].index]=cnt;  //注意是index值
            }
            else {//如果前面有挤完奶的
                poi.pop();//注意要pop出来
                poi.push(k);
                be[cow[i].index]=be[cow[p.x].index];                //注意这句话,仔细想一想为什么
            }
        }
        else {//如果是第一头牛
            poi.push(k);
            cnt++;
            be[cow[i].index]=cnt;
        }
    }
    cout<<cnt<<endl;
    for(register int i=1;i<=n;i++){
        printf("%d\n",be[i]);
    }
    return 0;
}
  • 总结:

    思维难度一般,考验一些小技巧的使用

转载于:https://www.cnblogs.com/Rye-Catcher/p/8643707.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值