4144.畜栏保留问题
总时间限制: 1000ms 内存限制: 65536kB
描述
农场有N头牛,每头牛会在一个特定的时间区间[A, B](包括A和B)在畜栏里挤奶,且一个畜栏里同时只能有一头牛在挤奶。现在农场主希望知道最少几个畜栏能满足上述要求,并要求给出每头牛被安排的方案。对于多种可行方案,主要输出一种即可。
输入
输入的第一行包含一个整数N(1 ≤ N ≤ 50, 000),表示有N牛头;接下来N行每行包含两个数,分别表示这头牛的挤奶时间[Ai, Bi](1 ≤ A≤ B ≤ 1, 000, 000)。
输出
输出的第一行包含一个整数,表示最少需要的畜栏数;接下来N行,第i+1行描述了第i头牛所被分配的畜栏编号(从1开始)。
样例输入
5
1 10
2 4
3 6
5 8
4 7
样例输出
4
1
2
3
2
4
分析
贪心策略
最大限度利用已有的畜栏。
- 把n头牛按照开始时间排序;
- 为第1头牛新建一个畜栏,记录其结束时间;
- 对于第i头牛,找到已有的畜栏中结束时间最早的,并与它自己的开始时间比较。
- 如果开始时间大于(注意这里是严格大于,之前自以为是大于等于然后一直WA嘤嘤嘤┭┮﹏┭┮)最早的结束时间,就把牛i安排到那个畜栏里面,把原来的牛j“丢出来”;如果小于等于,就新建一个畜栏。更新结束时间。
贪心策略正确性的证明
假设用以上贪心策略对前n头牛的安排方案至少和最优策略用的畜栏数相同,并且该方案具有最小的最早结束时间。
- 当n=1时,结论显然成立;
- 假设当n=k时也成立,那么对于k+1头牛:
① 证明贪心策略对前n头牛的安排方案至少和最优策略用的畜栏数相同:
(反证法)若畜栏数不是最优的,已知k头牛的最优策略的畜栏数就是上述贪心策略得到的,那么前k+1头牛的最优策略不会小于这个数;
又因为结论不成立,所以一定是——
贪心策略:放入第k+1头牛的时候,它的开始时间Ak+1小于前k头牛贪心策略得到的最早结束时间Tk,新建了一个畜栏;
最优策略:放入第k+1头牛的时候,它的开始时间大于等于前k头牛最优策略得到的最早结束时间T’k,放入了已有的畜栏;
也就是T’k<=Ak+1<Tk——这与归纳假设中前k头牛的贪心策略具有最小的最早结束时间矛盾!
② 证明贪心策略具有最小的最早结束时间:
当第k+1头牛的开始时间小于最小的最早结束时间,就新建一个畜栏,这是最早结束时间更新为第k+1头牛的结束时间(如果它比原有的更小),或者保持原来的最小最早结束时间不变;
否则,放入相应的畜栏,在所有的畜栏的结束时间中找到最小的,更新最早结束时间。
上述每一种更新,都保证了最早的结束时间是最小的。
贪心的实现
- 按照开始时间排序所有的牛;
- 用优先队列维护已有畜栏,保证堆顶的畜栏具有最小的最早结束时间;
对于一头新的牛,要么把原有的牛弹出,再放入自己(相当于更新了原有的畜栏的状态),要么直接放入自己(相当于新建了一个畜栏)。 - 注意此时的运算符重载在结构体内部
代码及解析
#include<iostream>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
#define MAXN 50010
struct cattle//定义结构体,l,r分别表示牛挤奶的开始、结束时间,id表示第i头牛
{
int l, r, id;
bool operator < (const cattle& a)const
{
return r > a.r;//保证优先队列的队首是结束时间最早的畜栏
}
};
priority_queue <cattle> q;//建立优先队列
cattle a[MAXN];//保存每一头牛的信息
int use[MAXN];//保存每一头牛被放入的畜栏编号
bool mycmp(cattle a, cattle b)//自定义排序,按照开始时间从小到大
{
if (a.l == b.l)return a.r < b.r;
return a.l < b.l;
}
主函数:
int main()
{
int n;//牛的总数
int ans = 0;//畜栏的总数
cin >> n;
memset(use, 0, sizeof(use));//归零(害其实关系不大)
while (!q.empty())q.pop();//清空
for (int i = 0; i < n; i++)
{
cin >> a[i].l >> a[i].r;
a[i].id = i;
}
sort(a, a + n, mycmp);//把n头牛按照开始时间从小到大进行排序
for (int i = 0; i < n; i++)
{
if (!q.empty() && q.top().r < a[i].l)//如果当前有畜栏并且可以用
{
use[a[i].id] = use[q.top().id];//把牛i放到这个畜栏里面
q.pop();//把原来的牛“丢出去”
}
else
{
ans++;//否则新建一个畜栏
use[a[i].id] = ans;//把牛i放到新的畜栏里面
}
q.push(a[i]);//把牛i放到原队列里面
}
cout << ans << endl;
for (int i = 0; i < n; i++)
cout << use[i] << endl;
return 0;
}
知识点
优先队列
- 基本概念
可以按照自定义的方式(数据的优先级)对队列中的数据动态排序
每次pop,push操作,队列都会根据优先级动态调整 - 优先级
默认使用<来确定数据之间的优先级关系
也可以由用户自定义
自定义的方式有两种:1. 不需要结构体(如下);2.在结构体内嵌运算符重载
priority_queue<int,vector<int>,less<int>>s;//定义优先级队列s,less表示按照递减(从大到小)的顺序插入元素 priority_queue<int,vector<int>,greater<int>>s;//定义优先级队列s,greater表示按照递增(从小到大)的顺序插入元素 priority_queue<int> p;//直接定义,默认为顺序递减 - 基本操作:
empty(); 队列为空返回1
pop(); 出队
push(); 入队
top(); 返回队列中优先级最高的元素
size(); 返回队列中元素的个数
基于多关键字的结构体比较
4. 内嵌运算符重载
决定了优先队列中的“优先级”规则
struct cattle//定义结构体,l,r分别表示牛挤奶的开始、结束时间,id表示第i头牛
{
int l, r, id;
bool operator < (const cattle& a)const
{
return r > a.r;//保证优先队列的队首是结束时间最早的畜栏
}
};
另一种定义方式
struct node
{
friend bool operator< (node n1, node n2)
{
return n1.priority < n2.priority;
}
int priority;
int value;
};
- 外置比较函数
比如传参到sort()函数里面使用
bool mycmp(cattle a, cattle b)//自定义排序,按照开始时间从小到大
{
if (a.l == b.l)return a.r < b.r;
return a.l < b.l;
}
sort(a, a + n, mycmp);//把n头牛按照开始时间从小到大进行排序
芜湖~第一篇题解写完啦!
本文介绍了如何解决畜栏保留问题,通过贪心策略和优先队列实现。首先将牛按开始时间排序,然后用优先队列维护畜栏,确保最小结束时间。贪心策略保证了畜栏数的最小化,并通过反证法证明其正确性。最后,文章给出了代码实现和优先队列的相关知识点。
1329

被折叠的 条评论
为什么被折叠?



