问题描述
有 n头牛(1<=n<=50,000)要挤奶。给定每头牛挤奶的时间区
间[A,B] (1<=A<=B<=1,000,000,A,B为整数)。
牛需要呆畜栏里才能挤奶。一个畜栏同一时间只能容纳一头牛。
问至少需要多少个畜栏,才能完成全部挤奶工作,以及每头牛都放哪个畜栏里(Special judged)
去同一个畜栏的两头牛,它们挤奶时间区间哪怕只在端点重合也是不可以的。
贪心算法
在这里,我们所有的奶牛都需要挤奶,到了一个奶牛的挤奶时,就必须要为这个奶牛寻找。
S(x)表示奶牛x的开始时间。E(x)表示x的结束时间。对E(x), x可以是奶牛,也可以是畜栏。畜栏的结束时间,就是正在其里面挤奶的奶牛的结束时间。同一个畜栏的结束时间是不断在变的。
解题思路
1:把奶牛按照挤奶开始时间从前往后排序
2:为第一头奶牛分配畜栏
3:依次处理后面的每一头奶牛,处理第i头奶牛时候,考虑已分配畜栏中结束时间最早的畜栏x。
若畜栏结束时间<奶牛开始时间,则不用分配新的畜栏,i可以进入x,并修改畜栏结束时间为E(i)
若畜栏结束时间>=奶牛开始时间,则分配新的畜栏,E(i)
直到所有奶牛处理结束
要点
需要用优先队列存放已经分配的畜栏,并使得结束时间最早的畜栏始终位于队列头部。
#include <iostream>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;
struct Cow {//奶牛
int a, b; //挤奶区间起终点
int No; //编号
bool operator<(const Cow& c) const {
return a < c.a;//按照开始时间从早到晚排序
}
} cows[50100];
int pos[50100]; //pos[i]表示编号为i的奶牛去的畜栏编号
struct Stall {//畜栏
int end; //结束时间
int No; //编号
bool operator<(const Stall& s) const {
return end > s.end;//按照畜栏的结束时间排序
}
Stall(int e, int n) :end(e), No(n) { }
};
int main()
{
int n;
cin>>n;
for (int i = 0; i < n; ++i) {
cin>>cows[i].a>>cows[i].b;
cows[i].No = i;
}
sort(cows, cows + n);
int total = 0;
priority_queue<Stall> pq;//畜栏的队列
for (int i = 0; i < n; ++i) {
if (pq.empty()) {//如果畜栏为空的话,就是放入第一个畜栏
++total;
pq.push(Stall(cows[i].b, total));//并且新开一个畜栏,以放入当前奶牛的结束时间为畜栏的结束时间
pos[cows[i].No] = total;
}
else {
Stall st = pq.top();//指向队列的顶端,即即将释放的畜栏
if (st.end < cows[i].a) { //端点也不能重合////如果畜栏的结束时间早于奶牛的开始时间的话
pq.pop();
pos[cows[i].No] = st.No;
pq.push(Stall(cows[i].b, st.No));
}
else { //对应 if( st.end < cows[i].a //畜栏的结束时间晚于奶牛的开始时间,即队列中没有可以释放的畜栏,就新开一个畜栏放入奶牛
++total;
pq.push(Stall{cows[i].b,total });//畜栏结束时间设置为奶牛结束
pos[cows[i].No] = total;
}
}
}
cout << total << endl;
for (int i = 0; i < n; ++i)
cout << pos[i] << endl;
return 0;
}