畜栏预定
题目
做法
我们现在考虑一种贪心方法,我们把所有牛的按\(l\)排序一遍,然后从小往大遍历,如果现在我的所有的畜栏都在工作,那么就新建一个畜栏,否则随便挑一个畜栏(反正都是没在工作,都一样),其实不难想是对的,但是严谨证明吗,我还是炒一下yxc大大的吧QMQ。
证明:
反证法,假设存在一种方案,使得需要的畜栏数量更少,记其需要的畜栏数量是 \(m\)。
考虑在上述做法中,第一次新建第 \(m+1\) 个畜栏的时刻,不妨设当前处理的是第 \(i\) 头牛。
由于所有牛是按开始时间从小到大排好序的,所以现在前 \(m\) 个畜栏中最后一头牛的开始时间一定小于等于第 \(i\) 头牛的开始时间。
而且前 \(m\) 个畜栏中最小的结束时间大于等于第 \(i\) 头牛的开始时间,所以前 mm 个畜栏里最后一头牛的吃草区间一定都包含第 \(i\) 头牛的开始时间,所以我们就找到了 \(m+1\) 个区间存在交集,所以至少需要 \(m+1\) 个畜栏,矛盾。
所以上述做法可以得到最优解,证毕。
当然,至于处理编号,以及看这个畜栏是否空闲,我是使用小根堆处理的,小根堆按照解放时间排序(即牛的结束时间+1)。
这里我又测试了一下,其实求最大覆盖数也能求出需要多少畜栏的,比如\([1,5]\)和\([2,6]\),\([2,5]\)的覆盖数都是\(2\),所以答案是\(2\),其实也不难想,对于两个区间有交集,就说明它们不能再同一个畜栏里,所以如果最多\(n\)个不能的话就要建立\(n\)个畜栏的。(不知道怎么证明,知道的可以回复我一下,我写进博客里面,虽然我个人是觉得是对的。)
个人这里给出一个较伪的证明,对于\(m\)个畜栏而言,我们考虑第二个栏中的每头牛,如果这头牛的区间和第一栏中没有一个是冲突的,就把它移动到第一栏中,这样第二栏的牛就在第一栏都有冲突了,第三栏则同时对一二栏做(优先对编号小的栏移动),这样第三栏的牛就在一二栏都存在冲突的牛(同时二对一也是如此,并没有改变),后面反复这样干,到了\(n\)号栏就变成了至少存在一头牛使得\(n\)个栏之间是冲突的。(如果不存在就至少会少一个栏。)
当然,这个做法并不能求编号,所以还是老老实实用前面的方法吧。
时间复杂度:\(O(nlogn)\)。
代码
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define N 51000
#define M 1100000
using namespace std;
typedef pair<int,int> PII;
struct node
{
int l,r,id;
}a[N];int n;
int b[M],ans=0,an[N],fuck[N];
inline bool cmp(node x,node y){return x.l<y.l;}
priority_queue<PII,vector<PII>,greater<PII> >c;//小根堆
int list[N],top=0;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++){scanf("%d%d",&a[i].l,&a[i].r);a[i].id=i;}
sort(a+1,a+n+1,cmp);
for(int i=1;i<=n;i++)
{
fuck[a[i].id]=i;
while(!c.empty() && c.top().first<=a[i].l)list[++top]=c.top().second,c.pop();
if(top>0)an[i]=list[top--];
else an[i]=++ans;
c.push(make_pair(a[i].r+1,an[i]));
}
printf("%d\n",ans);
for(int i=1;i<=n;i++)printf("%d\n",an[fuck[i]]);
return 0;
}
实验代码,即测试后面那个想法是不是对的代码(当然是对的啦,不然也不会写了):
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define N 51000
#define M 1100000
using namespace std;
typedef pair<int,int> PII;
struct node
{
int l,r,id;
}a[N];int n;
int b[M],ans=0,an[N],fuck[N];
inline bool cmp(node x,node y){return x.l<y.l;}
priority_queue<PII,vector<PII>,greater<PII> >c;
int list[N],top=0;
//试验区
int f[M];
inline int mymax(int x,int y){return x>y?x:y;}
///
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++){scanf("%d%d",&a[i].l,&a[i].r);a[i].id=i;f[a[i].l]++;f[a[i].r+1]--;}
int anss=0;
for(int i=1;i<=1000000;i++)anss=mymax(anss,f[i]+=f[i-1]);
sort(a+1,a+n+1,cmp);
for(int i=1;i<=n;i++)
{
fuck[a[i].id]=i;
while(!c.empty() && c.top().first<=a[i].l)list[++top]=c.top().second,c.pop();
if(top>0)an[i]=list[top--];
else an[i]=++ans;
c.push(make_pair(a[i].r+1,an[i]));
}
printf("%d\n",anss);
for(int i=1;i<=n;i++)printf("%d\n",an[fuck[i]]);
return 0;
}
小结
其实你会发现不管是这道题目还是上一道防晒,都是通过排序的方式解决到了\(l\)或者\(r\),然后处理另外一个的。