區間覆蓋問題

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][2,6][3,5][3,6][3,7][6,8]

2第一步選取[2,4],發現後面只能加入[6,8],以區間的個數爲2


貪心證明:因爲需要儘量多的獨立的線段,所以每個線段都儘可能的小,對於同一右端點,左端點越大,線段長度越小。那麼爲什麼要對右端點進行排序呢?如果左端點進行排序,那麼右端點是多少並不知道,那麼每一條線段都不能對之前所有的線段進行一個總結,那麼這就明顯不滿足貪心的最有字結構了。

3)區間選點問題

問題描述:給定一個長度爲m的區間,再給出n條線段和這n條線段需要滿足的要求(要求是這n條線段上至少有的被選擇的點的個數),問題是整個區間內最少選擇幾個點,使其滿足每一條線段的要求.

樣例:略

解題過程:將每個線段按照終點座標進行遞增排序,相同終點的前點座標大的在前面,一個個將其滿足

貪心證明:要想使得剩下的線段上選擇的點最少,那麼就應該儘量使得已經選擇了的點儘量能在後面的線段中發揮作用,而我們是從左往右選擇線段的,那麼要使得選取的點能滿足後面線段的要求,那麼必須是從線段的有端點開始選點,那麼問題(2)一樣涉及到一個問題,如果是按照線段的左端點對線段進行排序的話,不知道右端點的話,每一條線段都不能對之前已經操作過的所有線段進行一個總結,那麼這就同樣不滿足貪心算法的最優子結構性質了。

可以解決的實際問題:數軸上面有n個閉區間[a,b],取儘量少的點,使得每個區間內都至少有一個點(不同區間內含的點可以是同一個)

實例(類似第一種區間覆蓋,只不過求最大值)

三個農民每天清晨5點起牀,然後去牛棚給3頭牛擠奶。第一個農民在300時刻(從5點開始計時,秒爲單位)給他的牛擠奶,一直到1000時刻。第二個農民在700時刻開始,在 1200時刻結束。第三個農民在1500時刻開始2100時刻結束。期間最長的至少有一個農民在擠奶的連續時間爲900秒(從300時刻到1200時刻),而最長的無人擠奶的連續時間(從擠奶開始一直到擠奶結束)爲300時刻(從1200時刻到1500時刻)。  你的任務是編一個程序,讀入一個有N個農民(1 <= N <= 5000)擠N頭牛的工作時間列表,計算以下兩點(均以秒爲單位):  最長至少有一人在擠奶的時間段。  最長的無人擠奶的時間段。(從有人擠奶開始算起)

Input

Line 1:  一個整數N。  Lines 2..N+1:  每行兩個小於1000000的非負整數,表示一個農民的開始時刻與結束時刻。 

Output

一行,兩個整數,即題目所要求的兩個答案。

Sample Input

3
300 1000
700 1200
1500 2100

Sample Output

900 300

參考代碼:

//算法思想:區間覆蓋,先排序,總共就兩種情況。
#include<iostream>
#include<stdio.h>
#include<fstream>
#include<algorithm>
using namespace std;
#define Max(a,b) a>b?a:b
typedef struct
{
  int s_time;
  int e_time;
}D_farmers;
D_farmers df[5001];
int n;
bool cmp(D_farmers a,D_farmers b)//起始時間的升序排序
{
    return a.s_time<b.s_time||(a.s_time==b.s_time&&a.e_time<b.e_time);
}
int main()
{
    ifstream fin("milk2.in");
    ofstream fout("milk2.out");
    int i,max1_t=0,max2_t=0,s_max,t_max;//s_max和t_max分別記錄最大起始時間和結束時間
    fin>>n;
    for(i=0;i<n;i++)
        fin>>df[i].s_time>>df[i].e_time;

    sort(df,df+n,cmp);//升序排序
    s_max=df[0].s_time;
    t_max=df[0].e_time;
    max1_t=df[0].e_time-df[0].s_time;
    //max2_t=df[0].s_time;

    for(i=1;i<n;i++)
    {
     if(t_max>=df[i].s_time&&df[i].e_time>=t_max)//前一個結束時間大於當前起始時間,更新新的截至時間,
        t_max=df[i].e_time;
     else if(t_max<df[i].s_time)//前一個結束時間小於當前起始時間,進行判斷比較
      {
         //cout<<t_max<<endl;
         max1_t=Max(t_max-s_max,max1_t);
         //cout<<max1_t<<endl;
         max2_t=Max(df[i].s_time-t_max,max2_t);
         s_max=df[i].s_time;
         t_max=df[i].e_time;
      }

    }
    //cout<<max1_t<<" "<<max2_t<<endl;
    fout<<max1_t<<" "<<max2_t<<endl;
    return 0;
}


 

 

 

 
發佈了32 篇原創文章 · 獲贊 5 · 訪問量 16萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章