算法题(158):牛栏预定
审题:
本题需要我们安排奶牛的挤奶牛棚,使得每天每头奶牛都可以挤过奶,要求牛棚使用数量尽可能少思路:
方法一:贪心首先我们的奶牛放置之前应该先对奶牛的挤奶起始时间做升序排序,这样我们放置的时候才可以尽可能快的把开过的牛棚腾出来,从而达到提高牛棚利用率,减少牛棚使用数量
贪心策略:维护牛棚的使用结束时间,当我们需要给奶牛挤奶的时候就判断挤奶时间和当前使用最早结束时间的关系,根据判断结果来决定是开一个新牛棚还是使用使用完了的旧牛棚
解题:
#include<iostream> #include<algorithm> #include<queue> using namespace std; const int N = 5e4 + 10; struct node {int x;//挤奶起始时间/结束时间int y;//挤奶终止时间/牛棚编号int z;//奶牛初始编号bool operator<(const node& a) const{return x > a.x;} }a[N]; int n; int ret[N]; bool cmp(node& n1, node& n2) {return n1.x < n2.x; } int main() {//数据录入cin >> n;for (int i = 1; i <= n; i++){cin >> a[i].x >> a[i].y;a[i].z = i;}sort(a + 1, a + 1 + n, cmp);//奶牛挤奶起始时间升序排序priority_queue<node> p;//建立小根堆//放置奶牛int num = 1;//牛棚数量ret[a[1].z] = 1;p.push({a[1].y,num});for (int i = 2; i <= n; i++){int l = a[i].x;int r = a[i].y;if (l <= p.top().x)//无法在已有牛棚挤奶{num++;ret[a[i].z] = num;p.push({ r,num });}else//可以在已有牛棚挤奶{node m = p.top(); p.pop();ret[a[i].z] = m.y;p.push({ a[i].y,m.y });}}cout << num << endl;for (int i = 1; i <= n; i++){cout << ret[i] << endl;}return 0; }
详细步骤:
1.录入数据
2.对奶牛的起始时间进行升序排序
3.初始化小根堆,然后根据贪心策略对每头奶牛进行牛棚分配
4.数据输出
注意:
1.由于我们最后是要根据奶牛的编号顺序依次输出该编号奶牛所使用的牛棚编号,所以记录奶牛挤奶区间的节点需要有起始时间,结束时间和奶牛初始编号。
而对于负责记录牛棚使用情况的小根堆,则需要知道结束时间和牛棚编号
2.建立小根堆可以通过重载<运算符来实现,我们将<运算符的逻辑反过来,原本要放前面的数据放在后面。从而实现将大根堆转变为小根堆的目的
3.ret数组的索引一定是奶牛的初始编号,所以要用z属性
4.对于可以使用旧牛棚的情况,我们需要把旧牛棚的节点从小根堆中移除,然后进行完操作之后再将新的节点插入堆中
P2859 [USACO06FEB] Stall Reservations S - 洛谷