有 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。处理 i 时,考虑已分配畜栏中,结束时间最
早的畜栏x。
若 E(x) < S(i), 则不用分配新畜栏,i可进入x,并修改E(x)为E(i)
若 E(x) >= S(i),则分配新畜栏y,记 E(y) = E(i)
直到所有奶牛处理结束
需要用优先队列存放已经分配的畜栏,并使得结束时间最早的畜栏始终
位于队列头部。
证明:
由于按开始时间的顺序处理奶牛是必然,且按该算法,为奶牛i分配新
畜栏时,确实是不得不分配的,所以算法正确。
复杂度: O(nlogn)
#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; //<用sort排序是从小到大排序的
}
}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;
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%d%d",&cows[i].a,&cows[i].b);
cows[i].No=i;
}
sort(cows,cows+n);
int total=0;
priority_queue<Stall> pq;//priority_queue 优先队列,其底层是用堆来实现的。在优先队列中,队首元素一定是当前队列中优先级最高的那一个。
for(int i=0;i<n;i++)
{
if(pq.empty())//如果队列为空,则返回真值
{
++total;
pq.push(Stall(cows[i].b,total));//加入第一个奶牛的结束时间
pos[cows[i].No] = total;//更新编号为 cows[i].No的畜栏的编号
}
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(std.end < cows[i].a)
++total;
pq.push(Stall(cows[i].b,total)) ;
pos[cows[i].No] = total;
}
}
}
printf("%d\n",total);
for(int i=0;i<n;i++)
{
printf("%d\n",pos[i]);
}
return 0;
}
二、priority_queue
基本操作:
empty() 如果队列为空,则返回真
pop() 删除对顶元素,删除第一个元素
push() 加入一个元素
size() 返回优先队列中拥有的元素个数
top() 返回优先队列对顶元素,返回优先队列中有最高优先级的元素
在默认的优先队列中,优先级高的先出队。在默认的int型中先出队的为较大的数。
头文件:
#include <queue>
声明方式:
1、普通方法:
priority_queue<int> q; //通过操作,按照元素从大到小的顺序出队
priority_queue<int,vector<int>, greater<int> > q; //通过操作,按照元素从小到大的顺序出队
2、自定义优先级:
struct cmp {
operator bool ()(int x, int y)
{
return x > y; // x小的优先级高 //也可以写成其他方式,如: return p[x] > p[y];表示p[i]小的优先级高
}
};
priority_queue<int, vector<int>, cmp> q; //定义方法
//其中,第二个参数为容器类型。第三个参数为比较函数。
3、结构体声明方式:
struct node {
int x, y;
friend bool operator < (node a, node b)
{
return a.x > b.x; //结构体中,x小的优先级高
}
};
priority_queue<node>q; //定义方法
//在该结构中,y为值, x为优先级。
//通过自定义operator<操作符来比较元素中的优先级。
//在重载”<”时,最好不要重载”>”,可能会发生编译错误