Problem
题意翻译
FJ为他的奶牛们建造了一个游泳池,FJ认为这将有助于他们放松身心以及生产更多牛奶。
为了确保奶牛们的安全,FJ雇佣了N头牛,作为泳池的救生员,每一个救生员在一天内都会有一定的事情,并且这些事情都会覆盖一天内的一段时间。为了简单起见,泳池从时间t=0时开门,直到时间t=1000000000关门,所以每个事情都可以用两个整数来描述,给出奶牛救生员开始以及结束事情的时间。例如,一个救生员在时间t=4时开始事情并且在时间t=7时结束事情,那么这件事情就覆盖了3个单位时间。(注意:结束时间是“点”的时间)
不幸的是,FJ多雇佣了一名的救生员,但他没有足够的资金来雇佣这些救生员。因此他必须解雇一名救生员,求可以覆盖剩余救生员的轮班时间的最大总量是多少?如果当时至少有一名救生员的事情已经开始,则这个时段被覆盖。
输入格式
输入的第一行包括一个整数N(1≤N≤100000)。接下来N行中,每行告诉了我们一个救生员在0~10000000000范围内的开始以及结束时间。所有的结束时间都是不同的。不同的救生员的事情覆盖的时间可能会重叠。
输出格式
如果FJ解雇一名救生员仍能覆盖的最大时间。
Solution
线段覆盖类题型的常见思路就是根据左端点,从小到大进行。
如果有一个点,被其它的若干个线段覆盖,即它覆盖的区间也被其它的区间覆盖过,则这个区间一定作废,即最后输出所有区间覆盖的大小即可。
若不存在上述情况,我们需要通过线段之间的覆盖关系来查找究竟应该删除哪一个线段。
显然,删除的点一定是单独覆盖的点尽可能小,这个才能使总结果减少的尽量小,这样才能够保证结果最优。
在循环枚举的第i条线段,可以具体这么操作:
- 如果它不与前面的线段重合,对最后答案的贡献一定是 ;否则对答案的贡献是 表示前一个合法的线段的末尾端点;事实上进行画图,可以发现贡献=
- 若不包含下一个线段,单独占有的部分是 ,可以把前面的线段枪过来;然后再把前面线段单独占有的部分减掉。
这道题的主要思路是处理边与边之间的关系,事实上不需要很复杂的考虑,只要考虑相邻两条线段之间的关系即可。
代码如下:
#include<bits/stdc++.h>
using namespace std;
int n,flag=0,len=0;
struct node
{
int L;
int R;
int only;//单独对答案的贡献
};
node a[200000];
bool cmp(node p1,node p2)
{
return p1.L<p2.L;
}
int main(void)
{
freopen("lifeguards.in","r",stdin);
freopen("lifeguards.out","w",stdout);
scanf("%d",&n);
for (int i=1;i<=n;++i)
scanf("%d %d",&a[i].L,&a[i].R);
sort(a+1,a+n+1,cmp);
int last=0;
for (int i=1;i<=n;++i)
{
if (a[i].R<=last) flag=1;
else
{
len+=min(a[i].R-a[i].L,a[i].R-last);
a[i].only=min(a[i].R-a[i].L,a[i].R-last);
if (a[i].L<last) a[i-1].only-=last-a[i].L;
last=a[i].R;
}
}
int dec=INT_MAX;
for (int i=1;i<=n;++i)
dec=min(dec,a[i].only);
if (flag == 1) printf("%d\n",len);
else printf("%d\n",len-dec);
return 0;
}