基於貪心算法的幾類區間覆蓋問題:
(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 }