「AcWing学习记录」区间问题
AcWing 905. 区间选点
给定 N 个闭区间 [ai,bi],请你在数轴上选择尽量少的点,使得每个区间内至少包含一个选出的点。
输出选择的点的最小数量。
位于区间端点上的点也算作区间内。
思路:
- 将每个区间按右端点从小到大排序
- 从前往后依次枚举每个区间
- 如果当前区间中已经包含点,则直接pass
- 否则,选择当前区间的右端点
证明:
- Ans ≤ cnt
Ans 为正确答案(最小数量),而 cnt 为按照思路求解的结果,显然 Ans ≤ cnt。 - Ans ≥ cnt
从前往后依次枚举每个区间时,如果当前区间的左端点严格大于当前记录的右端点,则 cnt++,即找到了 cnt 个互不相交的区间,也就是至少要选择 cnt 个点,Ans ≥ cnt 得证。
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e5 + 10;
int n;
struct Range
{
int l, r;
bool operator< (const Range &rhs)const
{
return r < rhs.r;
}
}range[N];
int main()
{
scanf("%d", &n);
for(int i = 0; i < n; i++)
{
int l, r;
scanf("%d%d", &l, &r);
range[i] = {l, r};
}
sort(range, range + n);
int res = 0, ed = -2e9;
for(int i = 0; i < n; i++)
{
if(range[i].l > ed)
{
res++;
ed = range[i].r;
}
}
printf("%d\n", res);
return 0;
}
「洛谷 P1325」雷达安装
假设海岸线是一条无限延伸的直线。它的一侧是陆地,另一侧是海洋。每一座小岛是在海面上的一个点。雷达必须安装在陆地上(包括海岸线),并且每个雷达都有相同的扫描范围 d。你的任务是建立尽量少的雷达站,使所有小岛都在扫描范围之内。
数据使用笛卡尔坐标系,定义海岸线为 x 轴。在 x 轴上方为海洋,下方为陆地。
思路:
以每一座小岛为圆心,扫描范围 d 为半径作圆,那么所形成的每一个圆域与 x 轴相交都会形成一个区间,此时二维的计算几何问题就转化为了一维的区间选点问题。
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
const int N = 1e5 + 10;
int n, d;
struct Coord
{
int x, y;
}coord[N];
struct Range
{
double l, r;
bool operator< (const Range &rhs)const
{
return r < rhs.r;
}
}range[N];
int main()
{
cin >> n >> d;
for(int i = 0; i < n; i++)
{
int x, y;
scanf("%d%d", &x, &y);
coord[i] = {x, y};
}
for(int i = 0; i < n; i++)
{
auto tmp = coord[i];
if(tmp.y > d)
{
cout << "-1" << endl;
return 0;
}
double xx = sqrt(d * d - tmp.y * tmp.y);
double xl, xr;
xl = tmp.x - xx;
xr = tmp.x + xx;
range[i] = {xl, xr};
}
sort(range, range + n);
int res = 0;
double ed = -2e9;
for(int i = 0; i < n; i++)
{
if(range[i].l > ed)
{
res++;
ed = range[i].r;
}
}
cout << res << endl;
return 0;
}
AcWing 908. 最大不相交区间数量
给定 N 个闭区间 [ai,bi],请你在数轴上选择若干区间,使得选中的区间之间互不相交(包括端点)。
输出可选取区间的最大数量。
这题的思路和代码与上题完全一致。
证明:
- Ans ≤ cnt 反证法
假设 Ans > cnt,这就意味着可以选择出来比 cnt 更多个互不相交的区间,也就是要选择至少 Ans 个点才能把所有的区间覆盖到,而实际上我们之前选择的这 cnt 个点就已经把所有的区间都覆盖到了,即每一个区间都至少包含一个我们选择出来的点,这与实际情况不符,Ans ≤ cnt 得证。 - Ans ≥ cnt
Ans 为正确答案(最大数量),而 cnt 为按照思路求解的结果,显然 Ans ≥ cnt。
AcWing 906. 区间分组
给定 N 个闭区间 [ai,bi],请你将这些区间分成若干组,使得每组内部的区间两两之间(包括端点)没有交集,并使得组数尽可能小。
输出最小组数。
思路:
- 将每个区间按左端点从小到大排序
- 从前往后依次枚举每个区间,判断能否将其放到某个现有的组中 L[i] > Max_r
- 如果不存在这样的组,则开新组,然后再将其放进去
- 如果存在这样的组,将其放进去(在满足条件的组中任意挑一组),并更新当前组的 Max_r
证明:
- Ans ≤ cnt
Ans 为正确答案(最小组数),而 cnt 为按照思路求解的结果,显然 Ans ≤ cnt。 - Ans ≥ cnt
当枚举到第 cnt 个组时,即与前 cnt - 1 个组都有交集,此时 L[i] 一定大于每个组最右端区间的左端点(因为每个区间按左端点从小到大排序),同时小于等于每个组的 Max_r,所以一定会有 cnt 个区间存在交点 L[i],即至少需要 cnt 个组将已经枚举的区间完全分开,Ans ≥ cnt 得证。
(L[i] 为第 i 个区间的左端点,其中第 i 个区间为新开的第 cnt 个组的第 1 个区间)
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
const int N = 1e5 + 10;
int n;
struct Range
{
int l, r;
bool operator< (const Range &rhs)const
{
return l < rhs.l;
}
}range[N];
int main()
{
scanf("%d", &n);
for(int i = 0; i < n; i++)
{
int l, r;
scanf("%d%d", &l, &r);
range[i] = {l, r};
}
sort(range, range + n);
priority_queue<int, vector<int>, greater<int> > heap;
for(int i = 0; i < n; i++)
{
auto r = range[i];
if(heap.empty() || heap.top() >= r.l) heap.push(r.r);
else
{
heap.pop();
heap.push(r.r);
}
}
printf("%d\n", heap.size());
return 0;
}
AcWing 907. 区间覆盖
给定 N 个闭区间 [ai,bi] 以及一个线段区间 [s,t],请你选择尽量少的区间,将指定线段区间完全覆盖。
输出最少区间数,如果无法完全覆盖则输出 −1。
思路:
- 将每个区间按左端点从小到大排序
- 从前往后依次枚举每个区间,在所有能覆盖 start 的区间中,选择右端点最大的区间,然后将 start 更新成右端点的最大值
证明:
- Ans ≤ cnt
Ans 为正确答案(最小组数),而 cnt 为按照思路求解的结果,显然 Ans ≤ cnt。 - Ans ≥ cnt
Ans 中的区间一定能用 cnt 中的区间替换,并且替换完的方案仍然是合法的,所以 cnt 中的区间一定是最优解,即 Ans ≥ cnt。
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e5 + 10;
int n;
struct Range
{
int l, r;
bool operator< (const Range &rhs)const
{
return l < rhs.l;
}
}range[N];
int main()
{
int st, ed;
scanf("%d%d", &st, &ed);
scanf("%d", &n);
for(int i = 0; i < n; i++)
{
int l, r;
scanf("%d%d", &l, &r);
range[i] = {l, r};
}
sort(range, range + n);
int res = 0;
bool success = false;
for(int i = 0; i < n; i++)
{
int j = i, r = -2e9;
while(j < n && range[j].l <= st)
{
r = max(r, range[j].r);
j++;
}
//如果写成r <= st,那么出现st == ed的情况时会出错
if(r < st)
{
res = -1;
break;
}
res++;
if(r >= ed)
{
success = true;
break;
}
st = r;
i = j - 1;
}
if(!success) res = -1;
printf("%d\n", res);
return 0;
}