Stall Reservations
题目大概意思:
有 头牛,第 头牛的产奶时间为 , 一个房间在同一时间只能容纳一头牛产奶,问最少需要多少房间。输出最少需要的房间数,以及每头牛的产奶房间(编号从 开始)。
分析:
如果只问至少需要多少个房间,那么这个问题会简单很多,我们只需要统计每个时间点的正在产奶的牛的个数,然后输出最大值即可,可以用树状数组进行区间修改和单点询问来求出最大值,或运用差分序列,不过由于时间的取值范围高达 ,而时间限制只有 ,使用这种 或 O(M) 的算法很可能超出时间限制,虽然可以运用坐标离散化删除无用的时间点,或直接运用区间修改的线段树实现 ,将时间复杂度降低至 或 ,但比较麻烦,且不易记录每头牛的房间信息。
因此我们使用优先队列:先按照每头牛的产奶开始时间 进行非降序排序,从左到右依次遍历排好序的序列,用优先队列(priority_queue)存储在当前牛的产奶开始时间点 正在产奶的牛,按产奶结束时间点从小到大作为优先队列的排序依据。这样一来,在从左往右遍历的时候,我们可以记录下每个需要考虑的时间点的正在产奶的奶牛,并以此作为分配房间的依据。具体做法是把每头牛表示为一个结构体,记录 , 分别表示 产奶开始时间,产奶结束时间,牛的编号(输入顺序),牛的产奶房间号,用一个优先队列存储正在产奶的牛,一个队列(随便一个容器就可以)存储当前可用的房间的编号。在遍历过程中考虑当前牛之前,先令 (当前牛的 ) 的牛弹出优先队列,并将它使用的房间添加到可用房间的队列中。然后从可用房间的队列中取出一个房间号作为当前牛的产奶房间,即第 头牛的产奶房间,最后把当前牛添加到优先队列中。
时间复杂度是 , 只有 ,即使常数大一些也没有压力。
下面贴代码:
#include <cstdio>
#include <algorithm>
#include <queue>
using namespace std;
const int MAX_N = 50008;
// 表示牛的结构体
struct Cow
{
int A;
int B;
int numb;
int pos;
bool operator< (const Cow& _Right)const
{
return A < _Right.A;
}
Cow() {}
};
// 用作堆的比较函数
struct greater_B
{
bool operator() (const Cow& _Left, const Cow& _Right)const
{
return _Left.B > _Right.B;
}
};
Cow cows[MAX_N];
int ans[MAX_N];
int main()
{
int N;
scanf("%d", &N);
for (int i = 0; i < N; ++i)
{
Cow& cur = cows[i];
scanf("%d%d", &cur.A, &cur.B);
cur.numb = i;
}
sort(cows, cows + N);
priority_queue<Cow, vector<Cow>, greater_B> pq; // 存储正在产奶的牛的优先队列
queue<int> q; // 可用房间队列
int stall = 0; // 所需的最少房间数
for (int i = 0; i < N; ++i)
{
Cow& cur = cows[i];
while (!pq.empty() && pq.top().B < cur.A)
{
q.push(pq.top().pos); // 将已经产奶完成的牛使用的房间添加进可用房间的队列
pq.pop();
}
if (q.empty()) q.push(++stall); // 如果发现可用房间队列为空, 则令所需的最少房间数+1, 并将新的房间添加进去
cur.pos = q.front(); // 记录下当前牛使用的房间, 以便以后释放该房间
q.pop();
ans[cur.numb] = cur.pos; // 记录答案, 即令第 cur.numb 头牛使用的房间记为 cur.pos
pq.push(cur); // 将当前牛添加到正在产奶的牛的优先队列
}
printf("%d\n", stall);
for (int i = 0; i < N; ++i)
{
printf("%d\n", ans[i]);
}
return 0;
}