区间贪心

1.区间不相交问题

给出 N 个开区间(x, y),从中选择尽可能多的开区间,使得这些开区间两两没有交集。例如对于开区间(1, 3)、(2, 4)、(3, 5)、(6, 7)来说,可以选出最多三个区间(1, 3)、(3, 5)、(6, 7),它们之间没有交集。

思路分析

  • 最简单的情况(如图a),如果开区间 I1 被开区间 I2 包含,则选择 I1 是最好的选择,如果选择了 I1 ,就会有更大的区间如容纳其他开区间。
  • 将所有的开区间按左端点x从大到小排序,如果去掉区间包含的情况,那么一定会有 y1 > y2 > ... > yn 成立,如图b。可以看出I1的右边一段一定不会和其他区间重叠的,如果把它去掉,那么I1的左边的剩余部分就会被I2包含,因此应该选择 I1 。最终总是先选择左端点最大的区间。

image.png

代码

#include <cstdio> 
#include <algorithm>
#include <climits>

using namespace std;

const int N = 110;

struct Interval
{
    int x, y;
    bool operator< (const Interval &a) const
    {
        if (a.x != x) return x > a.x;
        else return y < a.y;
    }
    
}I[N];

int main()
{
    int n;

    scanf("%d", &n);
    
    for (int i = 0; i < n; i ++)
            scanf("%d%d", &I[i].x, &I[i].y);
    
    sort(I, I + n);
    
    int ans = 1, lastX = INT_MAX;
    for (int i = 0; i < n; i ++)
        if (I[i].y <= lastX)
        {
            lastX = I[i].x;
            ans ++;
        }
    
    printf("%d\n", ans);
    
    return 0;
}

2.区间选点问题

给定 N 个闭区间[x, y],求最少需要确定多少个点,才能使每个闭区间中都至少存在一个点。

思路分析

如果闭区间I1被闭区间I2包含,那么在I1中的点一定包含在I2中。将所有区间按左端点由大到小排序,接下来只取左端点,这样这个点就能覆盖尽可能多的区间。

代码

只需将上文代码中的I[i].y <= lastX改为I[i].y < lastX即可。
原因:在区间不相交问题中,区间使开区间,当端点重合时,两个区间并没有相交。在本问题中,区间是闭区间,端点重合时,当前区间一定包含上一个区间的左端点,因此可以忽略当前区间。

#include <cstdio> 
#include <algorithm>
#include <climits>

using namespace std;

const int N = 110;

struct Interval
{
    int x, y;
    bool operator< (const Interval &a) const
    {
        if (a.x != x) return x > a.x;
        else return y < a.y;
    }
    
}I[N];

int main()
{
    int n;

    scanf("%d", &n);
    
    for (int i = 0; i < n; i ++)
            scanf("%d%d", &I[i].x, &I[i].y);
    
    sort(I, I + n);
    
    int ans = 1, lastX = INT_MAX;
    for (int i = 0; i < n; i ++)
        if (I[i].y < lastX)
        {
            lastX = I[i].x;
            ans ++;
        }
    
    printf("%d\n", ans);
    
    return 0;
}
参考资料《算法笔记》.

雨天
0 声望3 粉丝

独自灿烂