演算法導論 16 1 活動選擇(貪心演算法)

2021-10-06 13:40:56 字數 3636 閱讀 4210

最優化問題的演算法往往都包含一系列的步驟,每個步驟都要做出最優化的選擇(相對全域性來說也是最優的);

貪心演算法所做的選擇看起來是當前最佳的,(相對於區域性來講是最佳的, 相對於全域性來講並不是最佳的),貪心演算法希望通過區域性的最優解得到乙個全域性最優解;

注意:貪心演算法有時候你能夠產生全域性最優解,有時候並不能產生全域性最優解;

貪心演算法是先做出選擇,然後再求解子問題,先選擇後求解會導致:

1) 由於先做選擇,選擇的比較標準不是子問題的解,因此求得的可能不是全域性最優解;

2) 由於經過了選擇之後再求解子問題,子問題的數目減少為o(1), 可以降低時間複雜度;

一種稱為擬陣的組合結構, 對於這種結構, 貪心演算法總是能夠給出最優解;

最小生成樹是貪心演算法的乙個經典的例子;

1) 尋找活動選擇問題的最優子結構

首先假設其子問題空間是一維的, 即s(1, i)的最優解由s(1, i-1)的最優解構成。但是發現s(1, i)的最優解可能是由s(2, i-1) 或是s(3,i-1)的最優解構成。因此,會發現子問題空間是一維是不夠的, 子問題空間是二維的;

其子問題空間是二維的,對於活動s(i, j), 需要做出j-i-1種選擇;

選擇活動ak(其中i<=k<=j), 那麼:

s(i, j) = s(i, k) + + s(k, j);

s(i, j) 具有最優化結構, 可以使用動態規劃演算法得到,時間複雜度是o(n^3)

假設c[i, j] 為問題s(i, j)中最大相容活動的個數,有遞迴式:

其邊界條件是當s(i, j)是空集的時候, c[i, j] = 0;

因此,完整的遞迴式是:

子問題空間是二維的, 對每個子問題都要程序o(n)次選擇,因此總的時間複雜度是o(n^3);

2) 將動態規劃轉化為貪心演算法

再次分析活動選擇問題, 有兩個發現:

對於任何非空子問題s(i, j), 設am是s(i, j)中具有最早結束時間的活動,那麼:

1) 活動am一定是s(i, j)的最大相容活動子集;

2) 子問題s(i, m)為空, 選擇了am,那麼子問題s(m, j)是唯一可能的非空;

首先了解, 最優子結構會隨著原問題最優解的子問題數目以及確定子問題是的選擇(受子問題空間以及每個子問題的選擇影響);

動態規劃中, 每個子問題有j-i-1種選擇;

貪心演算法中, 通過選擇集合中最早結束時間的活動,使得s(i, m)為空, 這樣就只用考慮子問題s(m, j). 在解決子問題中,貪心演算法只考慮了一種選擇,並且子問題空間也變為一維的;這樣時間複雜度就是o(n);

最優子結構會隨著原問題最優解的子問題數目以及確定子問題時的選擇數目變化;

此外,由於子問題空間將為一維,子問題的選擇只有一種,貪心演算法在這裡可以選用自頂向下解決,使得**更加簡單,時間複雜度從o(n^3)變為o(n);

貪心演算法求解活動選擇:

為了解決子問題s(i, j), 選擇s(i, j)中具有最早結束時間的am, 並將子問題s(m, j)的最優解中的活動集合加入到s(i, j)的解中去;

貪心演算法與動態規劃的區別:

仔細分析問題,簡化問題的子問題空間和選擇,使得最優化結構簡化,從而得到區域性最優解;

問題最優化結構的簡化會降低演算法的時間複雜度;

1) 使用動態規劃求解活動選擇問題

子問題空間是二維的s(i, j);

問題的選擇是在s(i, j)選擇乙個ak,之後原問題的解 s(i, j) = s(i, k) + + s(k, j);

分析原問題個子問題的型別,可以發現子問題的長度小於原問題的長度(j-i), 因此建議在外迴圈使用長度l,最先求解長度l =2的所有子問題,長度l = 1的問題是邊界條件;

確定選擇的是ak的時候, 還要分別確定s(i,k) 和s(k, j), 原則是字首子問題的最後乙個活動的結束時間要早於ak的開始時間, ak的結束時間要早於字尾子問題第乙個活動的開始時間;

#include

#include

#define n 11

int s[n]=;

int f[n]=;

//使用m陣列儲存(i, j)的活動選擇數

int m[n +1]

[n +1]

;void

dynamicactivityselect

(int low,

int high)}}

}int

main()

printf

("\n");

}return0;

}

2) 使用遞迴方式求解活動選擇問題

#include

#include

#define n 11

int s[n]=;

int f[n]=;

//使用m陣列儲存(i, j)的活動選擇數

int m[n +1]

[n +1]

;int

dynamicactivityselect

(int low,

int high)

return ret;

}int

main()

3) 使用貪心演算法求解活動選擇問題基於動態規劃演算法, 將其優化為貪心演算法,對於子問題s(i,j), 動態規劃是在(i, j) 之中選擇乙個k, 因此需要o(n)次選擇; 但是當我們嘗試優化選擇次數的時候, 發現對於s(i, j),總是選擇第乙個活動,即k = 1, 這樣s(i, j) = + s(i+1, j),可以將選擇次數變為1次;

進一步分析,原問題是s(i, j), 子問題是s(i+1, j), 其中j不變,問題的描述可以有s(i,j) 變為s(i, n); 其中n是最後乙個活動,這樣又可以將子問題空間優化為一維;

綜上所述, 貪心演算法的時間複雜度是o(n);

貪心演算法求解活動選擇的**如下:

#define max 11

int s[max]=;

int f[max]=;

//貪心演算法,逐個選擇活動,如果當前的活動沒有結束,就檢視下乙個回答的開始時間是否大於當前活動的結束時間

void

greedyactivityselection

(int i,

int j)

}printf

("\n");

}int

main()

4) 使用遞迴式的貪心演算法求解

#define max 11

int s[max]=;

int f[max]=;

intgreedyactivityselectionrecursive

(int i,

int j)

intmain()

演算法導論 貪心演算法 活動選擇問題

先給出乙個我寫的動態規劃方法的解法,求出最大活動集合中活動的個數。public class activitychoice public static int f public static int mmax 0 public static int activity int i,int j array...

貪心演算法 活動選擇

假設有乙個需要使用某一資源的活動組成的集合s,s n 1000 該資源一次只能被乙個活動占用,每乙個活動有乙個開始時間bi和乙個結束事件ei bi ei 若bi ej或者bj ei,則活動i和活動j相容。你的任務是 選擇由相互相容的活動組成的最大集合。輸入 輸入共n 1行,其中第1行為n,第2行到第...

貪心演算法 活動選擇

貪心演算法,選擇區域性最優解 活動選擇問題,每個活動有開始時間s,結束時間f,找到最大相容活動集。假設f按照大小順序排好。每次就從當前結束時間往後選最近的開始時間的活動 include include include using namespace std void activityselect m...