怎么理解
貪心法在解決這個問題的策略上目光短淺,僅僅依據當前已有的信息就做出選擇,並且一旦做出了選擇。無論將來有什么結果,這個選擇都不會改變。
一句話:不求最優,僅僅求可行解。
怎樣推斷
對於一個詳細的問題,怎么知道是否可用貪心算法解此問題,以及是否能得到問題的最優解?
我們能夠依據貪心法的2個重要的性質去證明:貪心選擇性質和最優子結構性質。
1、貪心選擇
什么叫貪心選擇?從字義上就是貪心也就是目光短線。貪圖眼前利益。在算法中就是僅僅依據當前已有的信息就做出選擇,並且以后都不會改變這次選擇。(這是和動態規划法的主要差別)
所以對於一個詳細問題。要確定它是否具有貪心選擇性質,必須證明每做一步貪心選擇是否終於導致問題的總體最優解。
2、最優子結構
當一個問題的最優解包括其子問題的最優解時,稱此問題具有最優子結構性質。
這個性質和動態規划法的一樣,最優子結構性質是可用動態規划算法或貪心算法求解的關鍵特征。
區分動態規划
動態規划算法通常以自底向上的方式解各子問題,是遞歸過程。
貪心算法則通常以自頂向下的方式進行,以迭代的方式作出相繼的貪心選擇,每作一次貪心選擇就將所求問題簡化為規模更小的子問題。
以二叉樹遍歷為例:
貪心法是從上到下僅僅進行深度搜索。也就是說它從根節點一口氣走到黑的,它的代價取決於子問題的數目,也就是樹的高度,每次在當前問題的狀態上作出的選擇都是1。不進行廣度搜索。所以終於它得出的解不一定是最優解。非常有可能是近似最優解。
而動態規划法在最優子結構的前提下,從樹的葉子節點開始向上進行搜索,而且在每一步都依據葉子節點的當前問題的狀況作出選擇,從而作出最優決策。所以她的代價是子問題的個數和可選擇的數目。它求出的解一定是最優解。
一般求解過程
使用貪心法求解能夠依據下面幾個方面進行(終於也相應着每步代碼的實現),以找零錢為例:
1、候選集合(C)
通過一個候選集合C作為問題的可能解。(終於解均取自於候選集合C)
比如。在找零錢問題中,各種面值的貨幣構成候選集合。
2、解集合(S)
每完畢一次貪心選擇,將一個解放入S。終於獲得一個完整解S
3、解決函數(solution)
檢查解集合S是否構成問題的完整解。
比如,在找零錢問題中。解決函數是已付出的貨幣金額恰好等於應付款。
4、選擇函數(select)
即貪心策略。這是貪心法的關鍵,選擇出最有希望構成問題的解的對象。
(這個選擇函數通常和目標函數有關)
比如,在找零錢問題中,貪心策略就是在候選集合中選擇面值最大的貨幣。
5、可行函數(feasible)
檢查解集合中增加一個候選對象是否可行。(增加下一個對象后是不是滿足約束條件)
比如。在找零錢問題中,可行函數是每一步選擇的貨幣和已付出的貨幣相加不超過應付款。
C的實現:(一般試題就是在這個基礎上加入詳細的實現)
Greedy(C) //C是問題的輸入集合即候選集合 { S={ }; //初始解集合為空集 while (not solution(S)) //集合S沒有構成問題的一個解 { x=select(C); //在候選集合C中做貪心選擇 if feasible(S, x) //推斷集合S中增加x后的解是否可行 S=S+{x}; C=C-{x}; } return S; }
小結
像找零問題,背包問題。近期臨點都是非常經典的貪心算法。並且都是實際的問題。理解上不太難,對於算法題,在理解算法思想的基礎上,多做題,查找規律,多總結一些C實現中重要的代碼段。