「USACO06FEB」Stall Reservations
题目链接 & 题面
貌似是 USACO 原创,但我实在找不到原创的链接,只能在洛谷、 AcWing 和 POJ 上找了
洛谷:https://www.luogu.com.cn/problem/P2859
AcWing:https://www.acwing.com/problem/content/113/
题目描述
有N头牛在畜栏中吃草。
每个畜栏在同一时间段只能提供给一头牛吃草,所以可能会需要多个畜栏。
给定N头牛和每头牛开始吃草的时间A以及结束吃草的时间B,每头牛在[A,B]这一时间段内都会一直吃草。
当两头牛的吃草区间存在交集时(包括端点),这两头牛不能被安排在同一个畜栏吃草。
求需要的最小畜栏数目和每头牛对应的畜栏方案。
输入格式
第1行:输入一个整数N。
第2..N+1行:第i+1行输入第i头牛的开始吃草时间A以及结束吃草时间B,数之间用空格隔开。
输出格式
第1行:输入一个整数,代表所需最小畜栏数。
第2..N+1行:第i+1行输入第i头牛被安排到的畜栏编号,编号是从1开始的 连续 整数,只要方案合法即可。
(洛谷上题目描述是挤奶,AcWing上是吃草。。。)
基本算法
贪心
应该是比较显而易见的一个贪心,我刚开始都不觉得自己有多贪。。
按开始的时间把牛排序,然后依次放入畜栏,每次尽量放在已经使用过的畜栏中,如果没有就新建一个畜栏,cnt++。
由于可以放进任意一个畜栏,可以直接选出结束时间最早的畜栏比较,如果比当前时间早就可以放入,如果比当前时间晚就新建。
因此,可以通过 std::priority_queue
来维护一个小根堆来存放畜栏使用情况(当然手写也可以
priority_queue
我一般管这个叫优先队列,它可以保证队头的元素是 最小/最大 的。
对于这道题,存放畜栏的优先队列需要存放 畜栏中牛结束的时间 和 畜栏的编号,其中需要将最小值放在队首的是结束时间。因此,可以使用 std::pair
来作为一个单位存放。对于 pair ,每个元素进行比较时,先比较 first ,所以可以把结束时间放在 first 中。
当然,手写一个node然后重载运算符来自定义比较也可以,但这就是 C++ 的进阶操作了。
另外,在写的时候,一定要搞清楚队列里要存什么,思路一定要清晰(我就是因为这个被绕住了)
代码
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <queue>
#include <functional>
using std::priority_queue;
using std::vector;
using std::sort;
using std::greater;
typedef std::pair<int,int> pint;
const int MAX=50005;
int n, cnt, le, ans[MAX];
priority_queue < pint, vector<pint>, greater<pint> > cl;
pint tmp;
struct Cow{
int start, end, nu;
}cow[MAX];
bool cmp(Cow x, Cow y) { return x.start < y.start; }
int main(){
freopen("data.in", "r", stdin);
freopen("data.out", "w", stdout);
scanf("%d", &n);
for(int i=0; i<n; i++){
scanf("%d %d", &cow[i].start, &cow[i].end);
cow[i].nu=i;
}
sort(cow, cow+n, cmp);
for(int i=0; i<n; i++){
if(!cl.empty() && cl.top().first<cow[i].start){
ans[cow[i].nu]=cl.top().second;
cl.pop();
tmp.first=cow[i].end; // tmp.second=cl.size();
tmp.second=ans[cow[i].nu];
cl.push(tmp);
}
else{
cnt++;
tmp.first=cow[i].end; tmp.second=cnt;
cl.push(tmp);
ans[cow[i].nu]=cnt;
}
}
printf("%d\n", cnt);
for(int i=0; i<n; i++) printf("%d\n", ans[i]);
return 0;
}