在《算法導論》中,講到高級算法的時候,認爲主要有三類,即:動態規劃、貪心算法和平攤分析。這裏通過一個經典小例子介紹一下貪心算法,可以讓我們極速掌握貪心算法的思想。
例:活動安排問題
有個需要在同一天使用同一個教室的活動,,,,教室同一時刻只能由一個活動使用。每個活動都有一個開始時間和結束時間 。一旦被選擇後,活動就佔據半開時間區間。如果和互不重疊,和兩個活動就可以被安排在這一天。這時也稱這兩個活動是相容的。該問題就是要安排這些活動使得儘量多的活動能不衝突的舉行。例如下圖所示的活動集合,其中各項活動按照結束時間單調遞增排序。
圖片: 活動安排時間表
解題思路:
首先,把各項活動按結束時間單調遞增排序。這樣做是有道理的,因爲可以用數學歸納法證明,用貪心算法求解的時候,最先結束的那個活動一定在解集裏面。具體證明方法在《算法導論》裏有,這裏略過不提。其實在直觀上也比較好理解,選擇了最早結束的那個活動,可以爲未安排的活動留下儘可能多的時間。
其次,我們來考慮貪心策略。上面說過,第一個活動已經選出來了,就是最早結束的那個活動。那麼第二個活動不妨就選跟它兼容的最先開始的活動就行了這個例子中就是,因爲。同理,下一個活動的選取,只要是開始時間大於的最近的一個就行了,依次類推。這就是貪心的策略,每次都選最近的。
以下是源程序:
#include<iostream>
using namespace std;
int Greedy(int len, int *s, int *f, bool *flag)
{
int i=0;
for(int j=1; j<len; j++)
{
if(s[j] >= f[i])
{
flag[j] = true;
i = j;
}
}
return 0;
}
int main()
{
int s[11] = {1, 3, 0, 5, 3, 5, 6, 8, 8, 2, 12};
int f[11] = {4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14};
bool flag[11] = {false};
flag[0] = true; //由於排序後,第一個活動肯定能被選中,所以將它的標誌置爲"真"。
Greedy(11, s, f, flag);
for(int i=0; i<11; i++)
{
if(flag[i])
{
cout<<i+1<<" ";
}
}
system("pause");
return 0;
}
在這段程序中,我們用flag[11]來標記每個活動是否被選中。程序運行結果爲:1 4 8 11