题意:
数轴上有 n个闭区间 [ai, bi],选择尽量少的区间覆盖一条指定线段 [1, t],不可能办到输出-1。
思路:
定义一个结构体代表区间,在读入数据后,对数据进行多关键字排序(第一是左端点小的在前,第二是右端点大的在前)。对数据进行过滤,左端点相同的区间只保留区间长度最大的,如果当前区间被之前保留的区间完全包含则去掉。
如果数组中第一个区间不包含1即不可能包含[1,t],直接返回-1。
定义一个变量point表示将需要覆盖的下一个点,即[point,t]还未被覆盖。当point<=t时,进行循环。比较当前区间的左端点值与point的大小,分三种情况:
<1>相等,表明当前区间应该被选,更新point,继续比较和循环。
<2>区间端点的左值较大,看上一个区间(该区间左端点的值一定小于等于point)。如果上一个区间的右端点值与当前区间的左端点值相差大于1,即不相接,说明所给的区间至少不能覆盖 [上一区间右端点,当前区间左端点] ,返回-1;
否则,上一个区间符合条件,应该被选,更新point,继续比较和循环。
<3>point较大,如果当前区间已经是最后一个区间,选择当前区间,如果选上之后还是不能覆盖t点,则返回-1,如果能,则循环结束。若不是最后一个区间,则接着循环判断下一个区间。
总结:
过滤区间是有必要的,没有用的区间会造成复杂的情况,并且增大时间代价。
在循环过程中,需要考虑到所有的情况,最好是根据某些值进行分类以免造成冗余或者缺失。
代码:
#include<stdio.h>
#include<algorithm>
using namespace std;
struct section
{
int left;
int right;
};
bool com(section a, section b)//左端点小的在前,右端点大的在前
{
if (a.left != b.left)
return a.left < b.left;
if (a.right != b.right)
return a.right > b.right;
}
int main()
{
int n, t;
scanf("%d %d", &n, &t);
section* sec = new section[n];
for (int i = 0; i < n; i++)
scanf("%d %d", &sec[i].left, &sec[i].right);
sort(sec, sec + n, com);
if (sec[0].left != 1)//不能覆盖区间左端点1
{
printf("-1");
return 0;
}
section* only = new section[n];
int left = sec[0].left;
int right = sec[0].right;
only[0] = sec[0];
int r = 1;
for (int i = 1; i < n; i++)//如果左区间相同保留右区间最大的一个,一个区间被另一个区间包含时不加入
{
if (sec[i].left > t)//超出要覆盖区间
break;
if (sec[i].left == left|| sec[i].right<=right)
continue;
else//新的左区间值
{
only[r] = sec[i];
left = sec[i].left;
right = sec[i].right;
r++;
}
}
int sum = 0;//记录已选择的区间数目
int point = 1;//[point,t]还没覆盖
int i = 0;//标记数组位置
while (point <= t)//未覆盖t
{
if (only[i].left == point)
{
point = only[i].right + 1;
sum++;
i++;
}
else if (only[i].left > point)//选择only[i-1]
{
if (only[i].left - only[i - 1].right > 1)//上一个区间接不住
{
printf("-1");
return 0;
}
sum++;
point = only[i - 1].right + 1;
}
else
{
if (i == r - 1)
{
sum++;
point = only[i].right + 1;
if (point <= t)
{
printf("-1");
return 0;
}
break;
}
else
i++;
}
}
printf("%d", sum);
}