题目链接
http://poj.org/problem?id=2376
题目大意
数轴上有 n (1<=n<=25000)个闭区间 [ai, bi],选择尽量少的区间覆盖一条指定线段 [1, t] (1<=t<=1,000,000)。覆盖整点,即(1,2)+(3,4)可以覆盖(1,4)。不可能办到输出-1。
题外话
以上WA和AC我均不清楚为啥。
#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
struct Tuple{
int a;
int b;
bool operator <(const Tuple &t) const
{
if (a != t.a) return (a < t.a);
else return (b < t.b);
}
bool in(int n)
{
return (n >= a && n <= b);
}
};
int N, T;
Tuple t[105];
int main ()
{
scanf("%d %d", &N, &T);
for(int i=0;i<N;i++) scanf("%d %d", &t[i].a, &t[i].b);
sort(t, t+N);
int n=0;
int point=0;
int p=0;
while((p<N) && (t[p].b<1)) p++; //排除掉无意义的点
while(point < T && p < N)
{
point++;
while((p<N) && !t[p].in(point)) p++;
int ppoint = point;
point = -1;
while((p<N) && t[p].in(ppoint))
{
point = max(t[p].b, point);
p++;
}
if(point == -1)
{
printf("-1\n");
return 0;
}
n++;
}
if(point >= T) printf("%d\n", n);
else printf("-1");
return 0;
}
后来,我觉得理清思路重写一份或许意义更大一点,很快就AC了。
基本思路
我们要选取最少的区间来覆盖这条线段。大体来讲,我们需要在保证覆盖的情况下,选择尽量"长"的区间。
我们不妨从左到右考虑。第一步,我们必然需要找到一个区间包含线段起点,并且这个区间越大越好,即寻找包含起点的右端点最靠后的区间(并不是真正的说这个区间最长)。这样,我们就完成了一部分任务,不妨把这一部分区间和被包含部分线段砍掉。这样,又有了起点,又可以使用刚才的思路。一直这样做,便能找到最少的区间来覆盖线段。
思路并不难,但是代码要怎么写呢。实际上,如果代码写的不够合理,将特别容易忽略若干"特殊"情况。
以下为代码描述,可跳过直接看代码。
n的取值范围之大迫使我们无法接受O(n 2 ^2 2)的复杂度。我们使用结构体Tuple来代表区间,然后对齐按左端点从小到大进行排序。然后,使用整型变量point来记录当前的起点(可以想象成被已经选中的区间覆盖的那部分线段被砍掉了),使用maxb(初值-1)记录包含point的区间的最大的右端点,使用整型n来记录选择区间个数。
然后从前往后遍历所有的区间。对于每一个区间:
- 如果这个区间的右端点比point还小,那这个区间没啥用,直接pass掉就好。
- 如果起点包含在这个区间之中,我们把这个区间的右端点和当前的最大右端点maxb进行比较,其中的最大值当做新的maxb。
- 如果这个区间的左端点都大于point,说明已经找不到可以包含point的区间了(因为我们曾按照左端点对区间进行了排序)。这个时候,如果maxb为-1,说明我们并没能找到包含point的区间,也就不可能完成覆盖。否则,我们就找到了一个包含point区间的最大右端点,选中这个区间。point 赋值为maxb+1,maxb赋值为-1。如果point已经大于T,说明已经完成了覆盖,直接退出循环。否则,我们需要重新访问这个区间,进行这三条分支判断,因为point已经更新。
循环结束后,我们还需判断maxb是否为-1,如果不是,那就再次更新point为maxb+1(注意题意中区间是代表一段连续的整数),选中区间数加1。
最后,我们判断point是否超过T,若超过,输出n;否则,输出-1。
完整代码
#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
struct Tuple{
int a;
int b;
bool operator <(const Tuple &t) const
{
return (a < t.a);
}
};
int N, T;
Tuple t[25005];
int main()
{
scanf("%d %d", &N, &T);
for(int i=0;i<N;i++)
{
scanf("%d %d", &t[i].a, &t[i].b);
}
sort(t, t+N);
int point = 1;
int maxb = -1;
int n=0;
for(int i=0;i<N;i++)
{
if(t[i].b < point) continue; //整个区间在要覆盖点的左边
else if((t[i].b >= point) && (t[i].a <= point)) // 区间包括要覆盖的点
{
maxb = max(maxb, t[i].b);
}
else //整个区间在当前点的右边
{
if(maxb == -1) //找不到能装下这个点的区间
{
break;
}
else
{
n++;
point = maxb+1;
maxb = -1;
if(point > T) break;
i--;
}
}
}
if(maxb != -1)
{
point = maxb+1;
n++;
}
if(point > T) printf("%d", n);
else printf("-1");
return 0;
}