区间贪心四种基础模型附加证明

区间贪心四种基础模型附加证明

尽可能少的点覆盖区间

题意

给定 N 个闭区间 \([a_i,b_i]\),在数轴上选择尽量少的点,使得每个区间内至少包含一个选出的点。

输出选择的点的最小数量。位于区间端点上的点也算作区间内。

思想

  1. 将每个区间按照右端点从小到大进行排序

  2. 从前往后依次枚举区间,ed值初始化为无穷小

    • 如果当前区间已近包含该点(ed > range[i].l),则直接pass
    • 否则,选择当前区间的右端点,ed = range[i].r,cnt++

证明

ans<=cntcnt 是一种可行方案, ans是可行方案的最优解,即最小值,所以一定成立。

ans>=cntcnt可行方案是一个区间集合,区间从小到大排序,两两之间不相交。所以覆盖每一个区间至少需要cnt个点,所以成立。

代码

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 100010;

int n;
struct Range
{
    int l, r;
    bool operator< (const Range &W)const
    {
        return r < W.r;
    }
}range[N];

int main()
{
    scanf("%d", &n);
    for (int i = 0; i < n; i ++ ) scanf("%d%d", &range[i].l, &range[i].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;
}

最大不相交区间数量

题意

给定 N 个闭区间 \([a_i,b_i]\),请你在数轴上选择若干区间,使得选中的区间之间互不相交(包括端点)。

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

思想和代码与上题一样

思想

  1. 将每个区间按照右端点从小到大进行排序

  2. 从前往后依次枚举区间,ed值初始化为无穷小

    • 如果当前区间已近包含该点(ed > range[i].l),则直接pass
    • 否则,选择当前区间的右端点,ed = range[i].r,cnt++

证明

ans<=cnt :反证法:假设 ans>cnt ,由最优解的含义 知,最多有 ans 个不相交的区间,因此至少需要 ans 个点才能 覆盖所有区间,而根据算法知,只需 cnt个点就能覆盖全部区 间,且cnt < ans,这与前边分析至少需要 ans个点才能徱 盖所有区间相矛盾,故ans<=cnt

ans>=cntcnt 是一种可行方案, ans是可行方案的最优解,即最大值,所以一定成立。

区间分组

题意

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

输出最小组数。

思想1 贪心

  1. 将每个区间按照左端点从小到大进行排序;

  2. 从前往后依次枚举区间,判断能否将其放到某个现有的组中,L[i] > max_r[j]

    • 如果不存在这样的组,则开新组,然后将其放进去;
    • 如果存在这样的组j,将其放进去,并更新当前组的max_r[j]

证明

ans<=cntcnt 是一种可行方案, ans是可行方案的最优解,即最小值,所以一定成立。

ans>=cnt

  • 反证法,假设存在一种方案,使得需要的组数更少, 记其需要的组数量是 \(cnt\)
  • 考虑在上述做法中,第一次新建第 \(j+1\) 个组的时刻,不妨设当前处理的是第 \(i\) 个区间。
  • 由于所有区间是按左端点从小到大排好序的,所以现在前 \(j\) 个组中最后区间左端点一定小于等于第 \(i\) 个区间的左端点。
  • 而且前 \(j\) 个组中最小的右端点大于等于第 \(i\) 个区间的左端点,所以前 \(j\) 个组里最后区间一 定都包含第 \(i\) 个区间的左端点,所以就找到了 \(m+1\) 个区间存在交集,所以至少需要 \(m+1\) 个组,矛盾。

思想2 扫描线

对于每一个区间:(左端点)++,(右端点 + 1)--,然后遍历一遍,求其前缀和,如图所示,过程中最大值即为答案

image-20220213131258508

但本题数据范围很大,另一种标记方法即可。左端点映射为奇数,右端点映射为偶数。

l : l * 2, r : r * 2 + 1

代码

#include <iostream>
#include <algorithm>
#include <queue>

using namespace std;

const int N = 100010;

int n;
struct Range
{
    int l, r;
    bool operator< (const Range &W)const
    {
        return l < W.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;
}
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 100100;

int n;
int b[2 * N], idx;

int main()
{
    scanf ("%d", &n);
    for(int i = 0; i < n; i ++)
    {
        int l, r;
        scanf("%d %d", &l, &r);
        b[idx ++] = l * 2;//标记左端点为偶数。
        b[idx ++] = r * 2 + 1;// 标记右端点为奇数。
    }

    sort(b, b + idx);

    int res = 1, t = 0;
    for(int i = 0; i < idx; i ++)
    {
        if(b[i] % 2 == 0) t ++;
        else t --;
        res = max(res, t);
    }
    printf ("%d\n", res);
    return 0;
}

区间覆盖

题意

给定 N个闭区间\([ai,bi]\)以及一个线段区间 \([s,t]\),请你选择尽量少的区间,将指定线段区间完全覆盖。

输出最少区间数,如果无法完全覆盖则输出 −1

思想

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

证明

在剩下所有能覆盖start的区间中,选择右端点最大的区间,则一定会比前面的选择最优,更快达到end,所以该做法一定是最优

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 100010;

int n;
struct Range
{
    int l, r;
    bool operator< (const Range &W)const
    {
        return l < W.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 ++ ;
        }

        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 @ 2022-02-13 14:01  pxlsdz  阅读(91)  评论(0编辑  收藏  举报