您的位置:首页 > 其它

基于贪心算法的几类区间覆盖问题

2017-10-28 21:06 369 查看
来源:http://www.cnblogs.com/dongsheng/archive/2013/04/19/3030444.html

基于贪心算法的几类区间覆盖问题
基于贪心算法的几类区间覆盖问题:

(1)区间完全覆盖问题

问题描述:

给定一个长度为m的区间,再给出n条线段的起点和终点(注意这里是闭区间),

求最少使用多少条线段可以将整个区间完全覆盖

样例:

区间长度8,可选的覆盖线段[2,6],[1,4],[3,6],[3,7],[6,8],[2,4],[3,5]

解题过程:

1、将每一个区间按照左端点递增顺序排列,拍完序后为[1,4],[2,4],[2,6],[3,5],

[3,6],[3,7],[6,8]

2、设置一个变量表示已经覆盖到的区域。再剩下的线段中找出所有左端点小于等于当前

已经覆盖到的区域的右端点的线段中,右端点最大的线段在加入,直到已经覆盖全部的区域

3、过程:

假设第一步加入[1,4],那么下一步能够选择的有[2,6],[3,5],[3,6],[3,7],

由于7最大,所以下一步选择[3,7],最后一步只能选择[6,8],这个时候刚好

达到了8退出,所选区间为3

4、贪心证明:

需要最少的线段进行覆盖,那么选取的线段必然要尽量长,而已经覆盖到的区域之前

的地方已经无所谓了,(可以理解成所有的可以覆盖的左端点都是已经覆盖到的地方),

那么真正能够使得线段更成的是右端点,左端点没有太大的意义,所以选择右端点来覆盖

(2)最大不相交覆盖(我总感觉这个算法不对,这不应该和会议安排问题一样吗? 直接按照终点排序再依次选择???)

问题描述:

给定一个长度为m的区间,再给出n条线段的起点和终点(开区间和闭区间处理的方法是

不同,这里以开区间为例),问题是从中选取尽量多的线段,使得每个线段都是独立的,

就是不和其它有任何线段有相交的地方

样例:

区间长度8,可选的覆盖线段[2,6],[1,4],[3,6],[3,7],[6,8],[2,4],[3,5]

解题过程:

对线段的右端点进行升序排序,每加入一个线段,然后选择后面若干个

(也有可能是一个)右端点相同的线段,选择左端点最大的那一条,如果加入以后不会

跟之前的线段产生公共部分,那么就加入,否则就继续判断后面的线段

1、排序:将每一个区间按右端点进行递增顺序排列,拍完序后为[1,4],[2,4],[3,5],[2,6],

[3,6],[3,7],[6,8]

2、第一步选取[2,4],发现后面只能加入[6,8],所以区间的个数为2

3、贪心证明:因为需要尽量多的独立的线段,所以每个线段都尽可能的小,

对于同一右端点,左端点越大,线段长度越小。那么为什么要对右端点进行排序呢?

如果左端点进行排序,那么右端点是多少并不知道,那么每一条线段都不能对之前所有

的线段进行一个总结,那么这就明显不满足贪心的最有字结构了。

(3)区间选点问题

问题描述:

给定一个长度为m的区间,再给出n条线段和这n条线段需要满足的要求

(要求是这n条线段上至少有的被选择的点的个数),问题是整个区间内最少

选择几个点,使其满足每一条线段的要求.

样例:略

解题过程:

将每个线段按照终点坐标进行递增排序,相同终点的前点坐标从大到小排列,

一个个将其满足(每次选择的点为该条线段的右端点)

贪心证明:

要想使得剩下的线段上选择的点最少,那么就应该尽量使得已经选择了的点尽量能

在后面的线段中发挥作用,而我们是从左往右选择线段的,那么要使得选取的点能

满足后面线段的要求,那么必须是从线段的右端点开始选点,那么问题(2)一样涉及

到一个问题,如果是按照线段的左端点对线段进行排序的话,不知道右端点的话,

每一条线段都不能对之前已经操作过的所有线段进行一个总结,那么这就同样不满足

贪心算法的最优子结构性质了。

可以解决的实际问题:数轴上面有n个闭区间[a,b],取尽量少的点,使得每个区间内都

至少有一个点(不同区间内含的点可以是同一个)

应用例题:(貌似不是很简单。。。)
  有一列整数,他的每一个数各不相同,我们不知道有多少个,但我们知道在
某些区间中至少有多少个整数,用区间(L,R,C)来描述,表示整数序列
中至少有C个整数来自子区间[L, R],若干个这样的区间,问这个整数序列的长
度最少能为多少。

区间选点算法实现:

#include <iostream>

#include <algorithm>

using namespace std;

struct line

{

    int left;

    int right;

}a[100];

bool cmp(line p, line q)

{

    if(p.right != q.right)

        return p.right < q.right;

    return p.left > q.left;

}

int main()

{

    int n;

    while(cin >> n)

    {

        for(int i = 0; i < n; ++i)

            cin >> a[i].left >> a[i].right;

        sort(a, a + n, cmp);

        int cnt = 0;

        int end = -1;

        for(int i = 0; i < n; ++i)

        {

            if(end >= a[i].left && end <= a[i].right)

                continue;

            else

            {

                ++cnt;

                end = a[i].right;

            }

        }

        cout << cnt << endl;

    }

    return 0;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息