基于贪心算法的几类区间覆盖问题:
(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],若干个这样的区间,问这个整数序列的长
度最少能为多少。
区间选点算法实现:
1 #include <iostream> 2 #include <algorithm> 3 4 using namespace std; 5 6 struct line 7 { 8 int left; 9 int right; 10 }a[100]; 11 12 bool cmp(line p, line q) 13 { 14 if(p.right != q.right) 15 return p.right < q.right; 16 return p.left > q.left; 17 } 18 19 int main() 20 { 21 int n; 22 while(cin >> n) 23 { 24 for(int i = 0; i < n; ++i) 25 cin >> a[i].left >> a[i].right; 26 sort(a, a + n, cmp); 27 int cnt = 0; 28 int end = -1; 29 for(int i = 0; i < n; ++i) 30 { 31 if(end >= a[i].left && end <= a[i].right) 32 continue; 33 else 34 { 35 ++cnt; 36 end = a[i].right; 37 } 38 } 39 cout << cnt << endl; 40 } 41 return 0; 42 }