题目描述
给定N个闭区间[ai,bi]以及一个线段区间[s,t],请你选择尽量少的区间,将指定线段区间完全覆盖。
输出最少区间数,如果无法完全覆盖则输出-1。
输入格式
第一行包含两个整数s和t,表示给定线段区间的两个端点。
第二行包含整数N,表示给定区间数。
接下来N行,每行包含两个整数ai,bi,表示一个区间的两个端点。
输出格式
输出一个整数,表示所需最少区间数。
如果无解,则输出-1。
数据范围
$$ 1\le N\le 10^5, \\\ −10^9\le ai\le bi\le 10^9, \\\ −10^9\le s\le t\le10^9 $$
输入样例
1 5
3
-1 3
2 4
3 5
输出样例
2
算法1
(排序后递归求解)
主要就两步:
1. 将输入的区间按左端点从小到大排序
2. 在所有左端点小于当前待覆盖区间起点的区间中,找出右端点最大的那个区间,选择它并将这个右端点作为下一次递归的待覆盖区间起点
记录选择次数就是答案
递,就硬递。
时间复杂度
$O(n\log n)$
C++ 代码
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
const int N = 100010;
int s, t, n, a, b;
vector<pair<int, int>> v;
bool cover(int s, int t, int idx, int& cnt) // idx是这次递归可以从v中选取区间的起点
{
if (s >= t)
true;
if(idx >= v.size() || v[idx].first > s)
return false;
int i=idx;
int c=i;
for (; i<v.size()&& v[i].first<=s; i++) // 从左端点小于起点的区间中选择右端点最靠右的
c = v[c].second<v[i].second?i:c;
if (v[c].second < s)
return false;
cnt+=1;
return cover(v[c].second, t, c+1, cnt);
}
int main()
{
cin >> s >> t;
cin >> n;
for (int i=0; i<n; i++)
{
cin >> a >> b;
v.push_back({a, b});
}
sort(v.begin(), v.end());
int cnt = 0;
if (!cover(s, t, 0, cnt))
cout << -1 << endl;
else
cout << cnt << endl;
}