三类基于贪心思想的区间覆盖问题
一、區(qū)間完全覆蓋問題
問題描述:給定一個(gè)長(zhǎng)度為m的區(qū)間,再給出n條線段的起點(diǎn)和終點(diǎn)(注意這里是閉區(qū)間),求最少使用多少條線段可以將整個(gè)區(qū)間完全覆蓋。
樣例:一個(gè)長(zhǎng)度為8的區(qū)間,可選的線段有[2,6],[1,4],[3,6],[3,7],[6,8],[2,4],[3,5]。
求解過程:
1、將每一條線段按左端點(diǎn)遞增順序排列,如果左端點(diǎn)相同,按右端點(diǎn)遞增順序排列,排完序后為[1,4],[2,4],[2,6],[3,5],[3,6],[3,7],[6,8];
2、設(shè)置一個(gè)變量表示已覆蓋到的區(qū)間右端點(diǎn),在剩下的線段中找出所有左端點(diǎn)小于等于當(dāng)前已覆蓋到的區(qū)間右端點(diǎn)的線段,選擇右端點(diǎn)最大并且大于當(dāng)前已覆蓋到的區(qū)間右端點(diǎn),重復(fù)以上操作直至覆蓋整個(gè)區(qū)間;
3、模擬過程:假設(shè)第一次加入[1,4],那么下一次能夠選擇的線段有[2,6],[3,5],[3,6],[3,7],由于3小于4且7最大,所以下一次選擇[3,7]進(jìn)行覆蓋,最后一次只能選擇[6,8],這個(gè)時(shí)候剛好覆蓋長(zhǎng)為8的區(qū)間-->break;即所選3條線段就能覆蓋長(zhǎng)度為8的大區(qū)間;
4、貪心證明:
要求用最少的線段進(jìn)行覆蓋,那么選取的線段必然要盡量長(zhǎng),而已覆蓋到的區(qū)域之前的地方已經(jīng)不用考慮了,可以理解成所有可覆蓋的左端點(diǎn)都已被覆蓋了,那么能夠使得線段更長(zhǎng)的取決于右端點(diǎn),左端點(diǎn)沒有太大的意義,所以選擇右端點(diǎn)來覆蓋。
題解報(bào)告:NYOJ #12?噴水裝置(二)
描述
有一塊草坪,橫向長(zhǎng)w,縱向長(zhǎng)為h,在它的橫向中心線上不同位置處裝有n(n<=10000)個(gè)點(diǎn)狀的噴水裝置,每個(gè)噴水裝置i噴水的效果是讓以它為中心半徑為Ri的圓都被潤(rùn)濕。請(qǐng)?jiān)诮o出的噴水裝置中選擇盡量少的噴水裝置,把整個(gè)草坪全部潤(rùn)濕。
輸入
第一行輸入一個(gè)正整數(shù)N表示共有n次測(cè)試數(shù)據(jù)。每一組測(cè)試數(shù)據(jù)的第一行有三個(gè)整數(shù)n,w,h,n表示共有n個(gè)噴水裝置,w表示草坪的橫向長(zhǎng)度,h表示草坪的縱向長(zhǎng)度。隨后的n行,都有兩個(gè)整數(shù)xi和ri,xi表示第i個(gè)噴水裝置的的橫坐標(biāo)(最左邊為0),ri表示該噴水裝置能覆蓋的圓的半徑。
輸出
每組測(cè)試數(shù)據(jù)輸出一個(gè)正整數(shù),表示共需要多少個(gè)噴水裝置,每個(gè)輸出單獨(dú)占一行。如果不存在一種能夠把整個(gè)草坪濕潤(rùn)的方案,請(qǐng)輸出0。
樣例輸入
2 2 8 6 1 1 4 5 2 10 6 4 5 6 5樣例輸出
1 2解題思路:典型的區(qū)間完全覆蓋問題。由于噴水裝置是安置在橫向中心線上并且圓具有對(duì)稱性,故只需取高度的一半,然后將每個(gè)噴水裝置能夠覆蓋的區(qū)間范圍映射成在x軸的長(zhǎng)度,然后按上面的方法貪心選線段即可。
AC代碼:
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=10005; 4 int t,n,k,cnt,pos,beg;double w,h,lb,xi,ri,maxv;pair<double,double> itv[maxn];bool flag; 5 int main(){ 6 while(cin>>t){ 7 while(t--){ 8 cin>>n>>w>>h;h/=2.0;pos=cnt=0;//h取一半 9 for(int i=0;i<n;++i){ 10 cin>>xi>>ri; 11 if(ri<h)continue;//ri==h也要算 12 itv[pos].first=xi-sqrt(ri*ri-h*h); 13 itv[pos++].second=xi+sqrt(ri*ri-h*h); 14 } 15 sort(itv,itv+pos);lb=0;beg=0;flag=false; 16 if(itv[0].first>0){cout<<0<<endl;continue;}//按左端點(diǎn)排序只需查看最左邊的端點(diǎn)是否滿足條件即可,最右邊的端點(diǎn)在下面有判斷 17 while(lb<w){ 18 maxv=0; 19 for(k=beg;k<pos&&itv[k].first<=lb;++k)//itv[k].first<=lb這樣保證整個(gè)區(qū)間是連續(xù)的,即草坪都會(huì)被潤(rùn)濕 20 maxv=max(maxv,itv[k].second);//找線段左端點(diǎn)在lb以內(nèi)右端點(diǎn)能覆蓋到的最遠(yuǎn)距離 21 if(maxv>lb)cnt++,lb=maxv,beg=k;//如果有一條線段右端點(diǎn)比當(dāng)前已覆蓋的區(qū)間右端點(diǎn)lb還大,那么就更新已覆蓋的右端點(diǎn)值,同時(shí)計(jì)數(shù)器加1 22 else {flag=true;break;}//否則說明不能覆蓋整個(gè)區(qū)間,直接退出,輸出0 23 } 24 if(flag)cout<<0<<endl; 25 else cout<<cnt<<endl; 26 } 27 } 28 return 0; 29 }
題解報(bào)告:NYOJ #6?噴水裝置(一)
描述
現(xiàn)有一塊草坪,長(zhǎng)為20米,寬為2米,要在橫中心線上放置半徑為Ri的噴水裝置,每個(gè)噴水裝置的效果都會(huì)讓以它為中心的半徑為實(shí)數(shù)Ri(0<Ri<15)的圓被濕潤(rùn),這有充足的噴水裝置i(1<i<600)個(gè),并且一定能把草坪全部濕潤(rùn),你要做的是:選擇盡量少的噴水裝置,把整個(gè)草坪的全部濕潤(rùn)。
輸入
第一行m表示有m組測(cè)試數(shù)據(jù);每一組測(cè)試數(shù)據(jù)的第一行有一個(gè)整數(shù)數(shù)n,n表示共有n個(gè)噴水裝置,隨后的一行,有n個(gè)實(shí)數(shù)ri,ri表示該噴水裝置能覆蓋的圓的半徑。輸出
輸出所用裝置的個(gè)數(shù)樣例輸入
2 5 2 3.2 4 4.5 6 10 1 2 3 1 2 1.2 3 1.1 1 2樣例輸出
2 5 解題思路:將每個(gè)能噴灑到草坪邊緣的噴水裝置的噴灑范圍映射成在x軸的長(zhǎng)度,然后按線段長(zhǎng)度遞增順序排列,再?gòu)暮笸柏澬倪x線段即可得到選擇最少的噴水裝置來潤(rùn)濕整個(gè)草坪。AC代碼: 1 #include<bits/stdc++.h> 2 using namespace std; 3 int t,n,cnt;double ans,ri,dt[605]; 4 int main(){ 5 while(cin>>t){ 6 while(t--){ 7 cin>>n;ans=0;cnt=0; 8 for(int i=0;i<n;++i)cin>>ri,dt[i]=ri>1?sqrt(ri*ri-1):0;//這里可以設(shè)置為0,因?yàn)轭}目已經(jīng)保證一定可以將草坪全部潤(rùn)濕 9 sort(dt,dt+n); 10 for(int i=n-1;ans<=10.0&&i>=0;--i)cnt++,ans+=dt[i];//從后往前能選出最少數(shù)量的噴水裝置,且一定能將草坪潤(rùn)濕 11 cout<<cnt<<endl; 12 } 13 } 14 return 0; 15 }?二、最大不相交區(qū)間數(shù)問題
問題描述:數(shù)軸上有n個(gè)區(qū)間$ [a_i,b_i] $,要求選擇盡量多個(gè)區(qū)間,使得這些區(qū)間兩兩沒有公共點(diǎn)。
樣例:數(shù)軸上有7個(gè)區(qū)間,可選的區(qū)間有[2,6],[1,4],[3,6],[3,7],[6,8],[2,4],[3,5]。
求解過程:
1、按區(qū)間右端點(diǎn)遞增順序排列,如果右端點(diǎn)相同,按左端點(diǎn)遞增順序排序,排完序后為[1,4],[2,4],[3,5],[2,6],[3,6],[3,7],[6,8];
2、第一次選擇[1,4],接下來只能選擇[6,8],即當(dāng)前數(shù)軸上最多只能選擇兩個(gè)不相交的區(qū)間。
3、貪心證明:為了選擇更多的區(qū)間個(gè)數(shù),先按區(qū)間右端點(diǎn)遞增順序排列,然后順序處理每個(gè)區(qū)間,如果它與當(dāng)前已選的所有區(qū)間都沒有相交,則選擇該區(qū)間,否則不選。接下來證明區(qū)間左端點(diǎn)a1,a2…對(duì)右端點(diǎn)沒有影響:
①當(dāng)a1>a2時(shí),區(qū)間2包含區(qū)間1,顯然不能選擇區(qū)間2,因?yàn)檫x擇區(qū)間1會(huì)留下更多的區(qū)域。不僅區(qū)間2如此,以后所有區(qū)間中只要有一個(gè)i滿足a1>ai,i都不要選,所以此種情況下,選擇區(qū)間1是明智的,與策略一致。
②排除情況1后,一定有a1<=a2<=a3……,此時(shí)選擇區(qū)間1是最優(yōu)策略,說明無論左端點(diǎn)是大是小,只要對(duì)區(qū)間右端點(diǎn)進(jìn)行排序,然后貪心選擇不相交的區(qū)間就可得到數(shù)軸上最多不相交的區(qū)間個(gè)數(shù),即這個(gè)策略是正確的。
?
題解報(bào)告:NYOJ #14?會(huì)場(chǎng)安排問題
描述
學(xué)校的小禮堂每天都會(huì)有許多活動(dòng),有時(shí)間這些活動(dòng)的計(jì)劃時(shí)間會(huì)發(fā)生沖突,需要選擇出一些活動(dòng)進(jìn)行舉辦。小劉的工作就是安排學(xué)校小禮堂的活動(dòng),每個(gè)時(shí)間最多安排一個(gè)活動(dòng)。現(xiàn)在小劉有一些活動(dòng)計(jì)劃的時(shí)間表,他想盡可能的安排更多的活動(dòng),請(qǐng)問他該如何安排。
輸入
第一行是一個(gè)整型數(shù)m(m<100)表示共有m組測(cè)試數(shù)據(jù)。
每組測(cè)試數(shù)據(jù)的第一行是一個(gè)整數(shù)n(1<n<10000)表示該測(cè)試數(shù)據(jù)共有n個(gè)活動(dòng)。
隨后的n行,每行有兩個(gè)正整數(shù)Bi,Ei(0<=Bi,Ei<10000),分別表示第i個(gè)活動(dòng)的起始與結(jié)束時(shí)間(Bi<=Ei)
輸出
對(duì)于每一組輸入,輸出最多能夠安排的活動(dòng)數(shù)量。
每組的輸出占一行
樣例輸入
2 2 1 10 10 11 3 1 10 10 11 11 20樣例輸出
1 2解題思路:按結(jié)束時(shí)間早進(jìn)行排序,然后貪心選擇不相交區(qū)間即可。
AC代碼: 1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long LL; 4 const int maxn=10005; 5 int t,n,tmp,ans;pair<int,int> itv[maxn]; 6 int main(){ 7 while(cin>>t){ 8 while(t--){ 9 cin>>n; 10 for(int i=0;i<n;++i)cin>>itv[i].second>>itv[i].first; 11 sort(itv,itv+n);tmp=-1;ans=0;//按結(jié)束時(shí)間早進(jìn)行排序 12 for(int i=0;i<n;++i) 13 if(tmp<itv[i].second)ans++,tmp=itv[i].first; 14 cout<<ans<<endl; 15 } 16 } 17 return 0; 18 }
三、區(qū)間選點(diǎn)問題
問題描述:數(shù)軸上有n個(gè)閉區(qū)間 $[a_i,b_i] $,要求選取盡量少的點(diǎn),使得每個(gè)區(qū)間內(nèi)都至少有一個(gè)點(diǎn)(不同區(qū)間內(nèi)含的點(diǎn)可以是同一個(gè))。
樣例略。
求解過程:
1、按左端點(diǎn)遞增順序排序,如果左端點(diǎn)相同,按右端點(diǎn)遞增順序排序,個(gè)人覺得這種比較好理解,當(dāng)然也可以按右端點(diǎn)遞增順序排序。
2、①?gòu)牡谝粋€(gè)區(qū)間右端點(diǎn)開始貪心往后找,如果下一個(gè)區(qū)間的左端點(diǎn)大于當(dāng)前已選區(qū)間的右端點(diǎn),說明要新開一個(gè)點(diǎn),計(jì)數(shù)器加1,同時(shí)更新右區(qū)間能覆蓋的最遠(yuǎn)距離;②如果下一個(gè)區(qū)間右端點(diǎn)小于當(dāng)前已選區(qū)間的右端點(diǎn),說明共享的線段范圍縮短了,那么就更新區(qū)間右端點(diǎn)為下一個(gè)區(qū)間右端點(diǎn),重復(fù)以上操作,直至篩選完所有區(qū)間。
貪心證明:為了選擇最少的點(diǎn)使得每個(gè)區(qū)間內(nèi)至少含有一個(gè)點(diǎn),考慮按區(qū)間左端點(diǎn)遞增順序排序,如果左端點(diǎn)相同,則按區(qū)間右端點(diǎn)遞增順序排序,然后以第一個(gè)區(qū)間右端點(diǎn)作為第一個(gè)點(diǎn)能覆蓋的最大范圍。①當(dāng)b1>b2時(shí),顯然此時(shí)一個(gè)點(diǎn)能覆蓋最大的區(qū)域右邊界變?yōu)閎2,同理,以后只要滿足 $ b_1 > b_i $,一個(gè)點(diǎn)能覆蓋的區(qū)域右邊界就會(huì)變?yōu)?$ b_i $,顯然這是正確的;②當(dāng)b1<a2時(shí),顯然一個(gè)點(diǎn)不能覆蓋到區(qū)間2上,所以需新開一個(gè)點(diǎn),此時(shí)能覆蓋的區(qū)域最右邊界變?yōu)閎2,同理,以后只要滿足 $ b1 < a_i $,則都要新開一個(gè)點(diǎn),并且其能覆蓋的區(qū)域右邊界將變?yōu)?$ b_i $,顯然這也是正確的;③?當(dāng)b1<b2時(shí),顯然區(qū)間1和區(qū)間2有公共的部分,但此時(shí)一個(gè)點(diǎn)能覆蓋的區(qū)域最右邊界還是為 b1,無需更新區(qū)域最右邊界,同理,對(duì)于以后只要滿足 $ b_1<b_i $,都無需新開一個(gè)點(diǎn),也無需更新能覆蓋區(qū)域的最右邊界,顯然這也是正確的。綜上,按區(qū)間左端點(diǎn)遞增的順序排序,再按規(guī)則貪心選點(diǎn)的策略是正確的。
題解報(bào)告:NYOJ #891 找點(diǎn)
描述
上數(shù)學(xué)課時(shí),老師給了LYH一些閉區(qū)間,讓他取盡量少的點(diǎn),使得每個(gè)閉區(qū)間內(nèi)至少有一個(gè)點(diǎn)。但是這幾天LYH太忙了,你們幫幫他嗎?
輸入
多組測(cè)試數(shù)據(jù)。
每組數(shù)據(jù)先輸入一個(gè)N,表示有N個(gè)閉區(qū)間(N≤100)。
接下來N行,每行輸入兩個(gè)數(shù)a,b(0≤a≤b≤100),表示區(qū)間的兩個(gè)端點(diǎn)。
輸出
輸出一個(gè)整數(shù),表示最少需要找?guī)讉€(gè)點(diǎn)。
樣例輸入
4 1 5 2 4 1 4 2 3 3 1 2 3 4 5 6 1 2 2樣例輸出
1 3 1解題思路:按左端點(diǎn)遞增順序排序,然后按上面的求解方法貪心選點(diǎn)即可。
AC代碼: 1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long LL; 4 const int maxn=105; 5 int n,tmp,ans;pair<int,int> itv[maxn]; 6 int main(){ 7 while(cin>>n){ 8 for(int i=0;i<n;++i)cin>>itv[i].first>>itv[i].second; 9 sort(itv,itv+n);tmp=itv[0].second;ans=1; 10 for(int i=0;i<n;++i){ 11 if(tmp<itv[i].first)ans++,tmp=itv[i].second; 12 else if(tmp>itv[i].second)tmp=itv[i].second; 13 } 14 cout<<ans<<endl; 15 } 16 return 0; 17 }
題解報(bào)告:poj 1328?Radar Installation
Description
Assume the coasting is an infinite straight line. Land is in one side of coasting, sea in the other. Each small island is a point locating in the sea side. And any radar installation, locating on the coasting, can only cover d distance, so an island in the sea can be covered by a radius installation, if the distance between them is at most d.?We use Cartesian coordinate system, defining the coasting is the x-axis. The sea side is above x-axis, and the land side below. Given the position of each island in the sea, and given the distance of the coverage of the radar installation, your task is to write a program to find the minimal number of radar installations to cover all the islands. Note that the position of an island is represented by its x-y coordinates.?
?
Figure A Sample Input of Radar Installations
Input
The input consists of several test cases. The first line of each case contains two integers n (1<=n<=1000) and d, where n is the number of islands in the sea and d is the distance of coverage of the radar installation. This is followed by n lines each containing two integers representing the coordinate of the position of each island. Then a blank line follows to separate the cases.?The input is terminated by a line containing pair of zeros?
Output
For each test case output one line consisting of the test case number followed by the minimal number of radar installations needed. "-1" installation means no solution for that case.Sample Input
3 2 1 2 -3 1 2 11 2 0 20 0Sample Output
Case 1: 2 Case 2: 1解題思路:典型的區(qū)間選點(diǎn),將雷達(dá)能覆蓋的范圍映射為x軸上的線段長(zhǎng)度,然后貪心區(qū)間選點(diǎn)即可。
AC代碼: 1 #include<iostream> 2 #include<algorithm> 3 #include<cmath> 4 using namespace std; 5 const int maxn=1005; 6 int n,d,ans,pos,cnt=1,x,y;double tmp; 7 struct node{double l,r;}point[maxn]; 8 bool cmp(node a,node b){return a.l<b.l;} 9 int main(){ 10 while(cin>>n>>d&&(n|d)){//注意這里:n|d,表示n和d同時(shí)為0時(shí),程序才退出 11 ans=1;pos=0; 12 for(int i=0;i<n;++i){ 13 cin>>x>>y; 14 if(y>d){ans=-1;continue;}//根號(hào)下只能為非負(fù)數(shù) 15 point[pos].l=1.0*x-sqrt(1.0*d*d-y*y);//以每個(gè)島嶼為圓心,半徑為d畫圓,其與x軸最后只有兩個(gè)交點(diǎn) 16 point[pos++].r=1.0*x+sqrt(1.0*d*d-y*y); 17 } 18 sort(point,point+pos,cmp);tmp=point[0].r; 19 for(int i=1;i<pos&&ans!=-1;++i){ 20 if(tmp<point[i].l){ans++;tmp=point[i].r;}//如果已選線段與當(dāng)前線段不相交,那么就設(shè)置一個(gè)新的雷達(dá),然后更新tmp為其右端點(diǎn)值 21 else if(tmp>point[i].r)tmp=point[i].r;//可以覆蓋掉下一條線段,但此時(shí)區(qū)間右端點(diǎn)縮短為下一條線段的右端點(diǎn),說明覆蓋的范圍縮短了 22 } 23 cout<<"Case "<<cnt++<<": "<<ans<<endl; 24 } 25 return 0; 26 }
?
轉(zhuǎn)載于:https://www.cnblogs.com/acgoto/p/9824723.html
總結(jié)
以上是生活随笔為你收集整理的三类基于贪心思想的区间覆盖问题的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用完成端口监控文件目录的例子
- 下一篇: 【转】数学与编程——求余、取模运算及其性