1. 程式人生 > >ACM知識點 之 貪心(2)選擇不相交區間

ACM知識點 之 貪心(2)選擇不相交區間

之前基本瞭解了貪心的基本思想,現在我們來看一下比較經典的幾個貪心問題。 
這篇文章討論的是”選擇不相交區間“,具體什麼意思,我們同樣先看一道題。

                            會場安排問題
                時間限制:3000 ms  |  記憶體限制:65535 KB
                            難度:4(這個難度真的很唬人~~)

描述 
學校的小禮堂每天都會有許多活動,有時間這些活動的計劃時間會發生衝突,需要選擇出一些活動進行舉辦。小劉的工作就是安排學校小禮堂的活動,每個時間最多安排一個活動。現在小劉有一些活動計劃的時間表,他想盡可能的安排更多的活動,請問他該如何安排。

輸入 
第一行是一個整型數m(m<100)表示共有m組測試資料。 
每組測試資料的第一行是一個整數n(n<1000)表示該測試資料共有n個活動。 
隨後的n行,每行有兩個正整數Bi,Ei(0<=Bi,Ei<10000),分別表示第i個活動的起始與結束時間(Bi<=Ei)

輸出 
對於每一組輸入,輸出最多能夠安排的活動數量。 
每組的輸出佔一行 
樣例輸入 


1 10 
10 11 

1 10 
10 11 
11 20 
樣例輸出 

2

提示 
注意:如果上一個活動在t時間結束,下一個活動最早應該在t+1時間開始

首先,把題目中的資訊轉換為模型。 
給你n個區間,求得不相交區間的數目。(P.S.也就是一句話的事~~題目太經典,無需多言)

下面我們來分析這道題是否能用貪心的方法來做,讓我們回顧貪心的三個關鍵點

1.問題可以分解為多個子問題。

那麼當前問題可不可以? 當然可以,選擇不相交區間的過程,完全可以分解為:給定一個區間,按照某種規則對接下來的多個區間進行比較,然後得出最優結果的過程。 
這裡的規則指的是:對於當前區間來說,優先選擇離當前區間距離最近而且區間最短的下一個區間。

2.總問題的最優解可由各個子問題的最優解得到。

這個也很好理解,由於每個子問題求得的永遠是離當前區間距離最近而且區間最短的下一個區間,而下一個區間也是按照這個規則來尋找另外一個區間的。所以對於最優解集合的解釋就是“對於每一個區間來說,後面的那個區間就是離它最近(空白少)

而且區間最短(佔用少)的”,這也就是說,滿足總體問題最優解的“儘可能多的選擇區間

3.子問題具有”無後效性“,當前子問題的求解不會影響到之前子問題的解。

這個題目乍一看貌似是不滿足無後效性的,因為給定的資料順序混亂,似乎每一次尋找都要進行一次遍歷,但是這個過程可以通過排序的方式解決。這種處理原始資料,使得處理過程更加簡單快捷的方法,叫做預處理

綜上,這道題(或者說這類問題)確實是可以通過貪心策略來取得最終的解得。 
那麼,預處理過程應該怎麼排序呢? 
我們再來聊一聊最優解的判定規則,也就是兩點: 
1.區間最短(長度) 
2.間隔最小(兩端位置)

那麼我們是按照哪一種指標來進行排序呢?答案很明顯,長度肯定是不靠譜的。因為單單通過長度進行排序,我們並不能對兩端位置進行控制,並沒有減少我們對於資料的判定過程,反而可能會加大難度。

那麼我們按照兩端位置進行排序,排序的規則很明顯,按照從小到大的順序進行排序。 
網路上很多人採用以後端點排序的方式,可能會讓剛接觸的人誤以為只能通過後斷電排序,其實不然,採用哪個端點排序都是可以的。而不管選擇哪一個來排序,其原理和本質都一樣,都是為了方便操作,將其有序化。

那麼我們的貪心策略可以用虛擬碼描述為:

while(剩餘區間的數目不為0)
{
    if(找到符合條件的下一個區間)
    {
        當前區間 = 下一個區間;
        答案數+1;
    }

    區間數--;
}


話說至此,這道題目我們就可以開始做了。

下面附上AC程式碼:

/*
************************************
    Title: NYOJ41--會場安排問題
************************************
    Date:2015/07/120
************************************
    author:劉旭
************************************
Memory:308KB
Time:80ms
************************************
*/

#include<stdio.h>
#include<algorithm>

using namespace std;

#define MAX 10050
struct Node
{
    int x,y;
} map[10005];  /*定義結構體陣列*/

int cmp(Node a,Node b)
{
    if(a.y!=b.y)
    {
        return a.y<b.y;
    }

    return a.x<b.x;
}

int main()
{
    int m = 0;
    scanf("%d",&m);
    while(m--)
    {
        int  num = 0;
        scanf("%d",&num);
        for(int i = 0; i < num; i++)
        {
            scanf("%d%d",&map[i].x,&map[i].y);
        }

        sort(map,map+num,cmp);

        int start = map[0].y;   ///起始位置
        int count=1;    ///答案數
        int num_last = num;///剩餘區間數目

        while(num_last > 0)
        {
            if(map[num - num_last].x > start)
            {
                count++;
                start = map[num - num_last].y;
            }

            num_last--;
        }

        printf("%d\n",count);
    }
    return 0;
}


行文至此,整個問題便已解決。說句實話,多寫寫分析什麼的,確實挺爽的~