问题描述:
数轴上有 n (1<=n<=25000)个闭区间 [ai, bi],选择尽量少的区间覆盖一条指定线段 [1, t]( 1<=t<=1,000,000)。覆盖整点,即(1,2)+(3,4)可以覆盖(1,4)。不可能办到输出-1。
input:
第一行:N和T
第二行至N+1行: 每一行一个闭区间。
output:
选择的区间的数目,不可能办到输出-1
样例输入:
3 10
1 7
3 6
6 10
样例输出:
2
解题思路:
首先,这个问题是一个贪心问题,首先要确定贪心求解策略,为尽量选择少的区间去覆盖一条指定线段,那么可以从起点最小的区间开始,选择同一起点上最长的区间,然后去遍历所有区间,选择一条起点在该区间内并且最长的区间,并且下一次遍历时更新起点为该区间,依次类推,知道覆盖完毕为止,否则不可能覆盖。
对于以上操作,我们可以进一步简化,由于要覆盖的区间是已知的,那么我们可以在输入数据时对数据就行处理,只保留要覆盖区间内的部分,这样我们保存的所有区间都是在要覆盖区间内的。
再输入完毕后要对所有区间进行排序,若起点不相同,就按起点升序拍,否则按终点升序拍,拍好序后,所有的区间都按数轴的前后顺序保存在数组中,如果最考前的区间,也就是数组第一个元素的起点,不是要覆盖区间的起点,那么不可能覆盖,否则,选择在起点的一个最长区间,进行遍历,然后找与起点区间有交叉的区间的最长区间 (!!注意!!这里最长区间的起点也可以是起点终点加1)
,更新起点,一直到覆盖位置,否则,找不到这样的区间,不能覆盖。
代码:
#include<cstdio>
#include<algorithm>
using namespace std;
struct section { //区间结构体
int start;
int end;
bool operator < (const section s)const {
if(start!=s.start) return start < s.start; //第一字典序升序
return end < s.end; //第二字典序升序
}
};
int n, t;
int main()
{
section ss[25000];
scanf("%d",&n);
scanf("%d",&t);
for (int i = 0; i < n; i++)
{
int b, e;
scanf("%d", &b);
scanf("%d", &e);
if(b < 1) //输入时对每个区间处理
b = 1;
if(e > t)
e = t;
ss[i].start=b;
ss[i].end=e;
}
sort(ss,ss+n);
if (ss[0].start > 1)
{
printf("-1"); //如果最靠前的区间起点不是1,不可能覆盖
}
else
{
int flag=0;
int down=ss[0].end;
for(int i=1;i<n;i++)
{
if(ss[i].start>1)
break;
if((ss[i].start==1)&&(ss[i].end>down))
{ //找到起点为1的最长区间
flag=i;
down=ss[i].end;
}
}
int up;
int number=1;
while(down!=t)
{
int qq=0;
up=down+1; //注意加一!!!
for(int i=flag;i<n;i++)
{
if(ss[i].start>up)
{
flag=i;
break;
}
else if(ss[i].start<=up&&ss[i].end>=down)
{ //找到符合要求的最长区间
qq=i;
down=ss[i].end;
}
}
if(up>down)//没有找到
{
number=-1;
break;
}
else{
flag=qq+1; //找到了,从此区间后遍历,此区间为起点
number++;
}
}
printf("%d\n",number);
}
}