「AcWing学习记录」区间问题

AcWing 905. 区间选点

原题链接

给定 N 个闭区间 [ai,bi],请你在数轴上选择尽量少的点,使得每个区间内至少包含一个选出的点。

输出选择的点的最小数量

位于区间端点上的点也算作区间内。

思路:

  1. 将每个区间按右端点从小到大排序
  2. 从前往后依次枚举每个区间
  • 如果当前区间中已经包含点,则直接pass
  • 否则,选择当前区间的右端点

证明:

  1. Ans ≤ cnt
    Ans 为正确答案(最小数量),而 cnt 为按照思路求解的结果,显然 Ans ≤ cnt。
  2. 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],请你在数轴上选择若干区间,使得选中的区间之间互不相交(包括端点)。

输出可选取区间的最大数量

这题的思路和代码与上题完全一致。

证明:

  1. Ans ≤ cnt 反证法
    假设 Ans > cnt,这就意味着可以选择出来比 cnt 更多个互不相交的区间,也就是要选择至少 Ans 个点才能把所有的区间覆盖到,而实际上我们之前选择的这 cnt 个点就已经把所有的区间都覆盖到了,即每一个区间都至少包含一个我们选择出来的点,这与实际情况不符,Ans ≤ cnt 得证。
  2. Ans ≥ cnt
    Ans 为正确答案(最大数量),而 cnt 为按照思路求解的结果,显然 Ans ≥ cnt。

AcWing 906. 区间分组

原题链接

给定 N 个闭区间 [ai,bi],请你将这些区间分成若干组,使得每组内部的区间两两之间(包括端点)没有交集,并使得组数尽可能小。

输出最小组数

思路:

  1. 将每个区间按左端点从小到大排序
  2. 从前往后依次枚举每个区间,判断能否将其放到某个现有的组中 L[i] > Max_r
  • 如果不存在这样的组,则开新组,然后再将其放进去
  • 如果存在这样的组,将其放进去(在满足条件的组中任意挑一组),并更新当前组的 Max_r

证明:

  1. Ans ≤ cnt
    Ans 为正确答案(最小组数),而 cnt 为按照思路求解的结果,显然 Ans ≤ cnt。
  2. 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。

思路:

  1. 将每个区间按左端点从小到大排序
  2. 从前往后依次枚举每个区间,在所有能覆盖 start 的区间中,选择右端点最大的区间,然后将 start 更新成右端点的最大值

证明:

  1. Ans ≤ cnt
    Ans 为正确答案(最小组数),而 cnt 为按照思路求解的结果,显然 Ans ≤ cnt。
  2. 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;
}
posted @ 2023-01-03 22:44  恺雯  阅读(45)  评论(0编辑  收藏  举报