貪心算法
貪心算法通過一系列的選擇來得到問題的解。它所做的每一個選擇都是當前狀態下局部的最好選擇,即貪心選擇。
貪心選擇的一般特征:貪心選擇性質和最優子結構性質。
貪心選擇性質:
所謂貪心選擇性質是指所求問題的整體最優解可以通過一系列局部最優的選擇,即貪心選擇來達到。這是貪心算法可行的第一個基本要素,也是貪心算法與動態規划算法的主要區別。在動態規划算法中,每步所做的選擇往往依賴於相關子問題的解。因而只有在解出相關子問題后,才能做出選擇。而在貪心算法中,僅在當前狀態下做出最好選擇,即局部最優選擇。然后再去解出做出這個選擇后產生的相應的子問題。貪心算法所做的貪心選擇可以依賴於以往所做過的選擇,但決不依賴於將來所做的選擇,也不依賴於子問題的解。正是由於這種差別,動態規划算法通常以自底向上的方式解各個問題,而貪心算法則通常以自頂向下的方式進行,以迭代的方式做出相繼的貪心選擇,沒做一次貪心選擇就將所求問題簡化為規模更小的子問題。
對於一個具體問題,要確定它是否具有貪心選擇性質,必須證明每一步所做的貪心選擇最終導致問題的整體最優解。
最優子結構性質:
當一個問題的最優解包含其子問題的最優解時,稱此問題具有最優子結構性質。
彈性算法與動態規划算法的差異:
貪心算法和動態規划算法都要求問題具有最優子結構性質,這是兩類算法的一個共同點。大多數時候,能用貪心算法求解的問題,都可以用動態規划算法求解。但是能用動態規划求解的,不一定能用貪心算法進行求解。
活動安排問題:
問題:
設有n個活動的集合E={1,2,...,n},其中每個活動都要求使用同一資源,如演講會場等,而在同一時間內只有一個活動能使用這一資源。每個活動i都有一個要求使用該資源的起始時間Si和一個結束時間Fi,且Si<Fi。如果選擇了活動i,則它在半開時間區間[Si,Fi)內占用資源。若區間[Si,Fi)與區間[Sj,Fj)不相交,則稱活動i與活動j是相容的。也就是說Si>=Fj或Sj>=Fi時,活動i與活動j相容。活動安排問題就是要在所給的活動集合中選擇出最大的相容活動子集合。
關於證明:
這個問題可以使用貪心算法進行求解。其實這個問題的關鍵並不是如何用貪心算法求解,而是如何證明這個問題可以用貪心算法求解。鑒於證明的復雜性,還是不在此討論證明問題。其實貪心算法問題的證明無非都是用數學歸納法證明,不錯就是那個傳說中萬能證明法,數學歸納法。還是直接討論實現過程吧。
實現:
首先將活動按照活動的結束時間非遞減:F1<=F2<=...<=Fn排序。如果所給的活動未排序,則先將活動按此序排列,時間復雜度一般為O(nlogn)可解決。以下是解決問題的算法。
1 //貪心算法-活動安排的實現 2 3 #include "stdafx.h" 4 #include "stdio.h" 5 6 template<class Type> 7 void GreedySelector(int n,Type s[],Type f[],bool A[]) 8 { 9 A[0]=1; //第一個直接選取 10 int j=0; 11 for(int i=1;i<n;i++) 12 { 13 if(s[i]>=f[j]){A[i]=true;j=i;} //如果第i+1的活動的開始時間大於或等於第i個活動的結束時間,則標記該活動 14 else A[i]=false; 15 } 16 } 17 18 int _tmain(int argc, _TCHAR* argv[]) 19 { 20 //初始化數據 21 int n=3; 22 int s[3]={1,2,4}; //開始時間 23 int f[3]={3,3,5}; //結束時間 24 bool A[3]={0,0,0}; //數組A用於標記是否選擇活動,1表示選擇,0表示不選擇 25 26 GreedySelector<int>(n,s,f,A); 27 for(int i=0;i<n;i++) 28 { 29 printf("%d\n",A[i]); 30 } 31 }
該算法的貪心選擇的意義是使剩余的可安排的時間段極大化,以便安排盡可能多的相容活動。也就是說,每次選擇完了之后,保證這次的選擇之后留下的時間是最多的。
測試結果
時間復雜度:
GreedySelector算法效率極高。當輸入的數據是已經按照結束時間非遞減排序好的時候,算法只需要O(n)的時間安排n個活動,使最多的活動能相容地使用公共資源。
適用范圍:
貪心算法並不能總求得問題的整體最優解。但對於某些問題,卻總能求得整體最優解,這要看問題時什么了。只要能滿足貪心算法的兩個性質:貪心選擇性質和最優子結構性質,貪心算法就可以出色地求出問題的整體最優解。即使某些問題,貪心算法不能求得整體的最優解,貪心算法也能求出大概的整體最優解。如果你的要求不是太高,貪心算法是一個很好的選擇。最優子結構性質是比較容易看出來的,但是貪心選擇性質就沒那么容易了,這個時候需要證明。證明往往使用數學歸納法。
適用范圍個人總結的一句話,能證明用就可以用。(因為個人覺得證明比算法實現還難)