题目描述
数轴上有 n (1<=n<=25000)个闭区间 [ai, bi],选择尽量少的区间覆盖一条指定线段 [1, t]( 1<=t<=1,000,000)。
覆盖整点,即(1,2)+(3,4)可以覆盖(1,4)。
不可能办到输出-1
输入
第一行:N和T
第二行至N+1行: 每一行一个闭区间。
输出
选择的区间的数目,不可能办到输出-1
样例输入
3 10
1 7
3 6
6 10
样例输出
2
提示
这道题输入数据很多,请用scanf而不是cin
解题思路
题目要在给出的 n 个闭区间内选出最少的区间数,使得选出的区间能够覆盖 [1,t] 范围。因此,从 1 到 t 的每个数,至少都有一个区间覆盖到。
首先我们对输入的线段进行第一按起始点升序第二按线段终点排序的多关键字排序,结果存储到链表中。接着每次从链表中取出一条线段,我们判断这条线段的起始点是否大于上一个区间的起始点,如是,接着判断取出线段的起始点是否大于上一个区间的终点+1,如果是的话,cmax+1 这个点覆盖不了,说明是无解的。否则就将将下一个区间起始点 cmin 设为上一区间终点 cmax +1,将下一个区间终点 cmax 设为取出线段的终点 cb ,同时计数 point++ 。
回到一开始的判断,如果取出线段的起始点不大于上一个区间的起始点,那么我们考虑当前取出的这条线段是否比原来选中的区间的可到达范围更广,即当前区间的右端点能延长,如是,将 cmax 设为 cb 。如此循环,直到 cmax >= t ,即找到了所有需要区间,此时就输出最后的计数结果 point 。
程序源码
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <list>
#include<stdio.h>
using namespace std;
struct line { //线段结构体
int pa;
int pb;
line(int s, int e) :pa(s), pb(e) {
}
bool operator<(const line& l2) { //多关键字排序
if (pa != l2.pa) { //第一关键字 起始点pa升序
return pa < l2.pa;
}
return pb > l2.pb; //第二关键字 终点pb降序
}
};
int main(int argc, char** argv) {
list<line> lines;
int n, t;
cin >> n >> t;
for (int i = 0; i < n; i++) { //获取输入数据
int a, b;
scanf("%d%d", &a, &b);
if (b < a) swap(a, b);
if (a > t)continue;
line newline(a, b); //把点作为line存到链表中
lines.push_back(newline);
}
lines.sort(); //排序,按第一起始点pa升序,第二终点pb降序排序
int cmin = 0, cmax = 0, nmin = 0, nmax = 0, ca = 0, cb = 0, point = 0;
for (list<line>::iterator it = lines.begin(); it != lines.end(); it++) {
ca = it->pa; cb = it->pb;
if (ca > cmin) { //取出线段的起始点大于上一个区间的起始点
if (ca > cmax + 1) { //取出线段的起始点大于上一个区间的终点+1
cout << -1 << endl; //cmax+1 这个点覆盖不了,无解
return 0;
}
cmin = cmax + 1; //将下一个区间起始点设为上一区间终点+1
cmax = cb; //将下一个区间终点设为取出线段的终点
point++; //计数+1
if (cmax >= t) break; //已达到要求,返回
continue;
}
else {
if (cb > cmax) cmax = cb; //当前区间的右端点能延长
if (cmax >= t) break; //已经覆盖完要求的区间
}
}
if (cmax < t) {
cout << -1 << endl;
return 0;
}
cout << point << endl;
}
本文介绍了一种在数轴上选择最少区间覆盖指定线段的算法。通过多关键字排序和逐个检查区间的策略,确保了覆盖效果的同时最小化了区间的数量。

被折叠的 条评论
为什么被折叠?



