貪心法:
貪⼼算法(又稱貪婪算法)是指,在對問題求解時,總是做出在當前看來是最好的選擇。也就是說,不從整體最優上加以考慮,他所做出的僅是在某種意義上的局部最優解。
貪心算法不是對所有問題都能得到整體最優解,關鍵是貪⼼心策略的選擇,選擇的貪⼼策略必須具備⽆后效性,即某個狀態以后的過程不會影響以前的狀態,只與當前狀態有關。
太概念化了。總結起來三點:
可行性:必須滿足問題的約束
局部最優:當前步驟中所有可行的選擇里最佳的局部選擇。
不可取消:選擇一旦做出,后面的步驟就無法改變。
問題要具有貪心選擇性:問題的最優解可以通過一系列的局部最優選擇來達到。(最重要的一步,決定這個問題是否可以用貪心法來解決,此處的解決特指找到最優解)。
最優子結構性質:指一個問題的最優解一定要包含子問題的最優解。
貪心和DP的差別在哪呢,首先他萌確實都有最優子結構的性質,但是DP通常是以自底向上的方式解決各個子問題(如22中的整裝待發問題就是從底部的每一層逐漸建立起那個二維數組),而貪心的方法通常是自頂向上的。
均分紙牌問題的分析:
均分紙牌問題:有 N 堆紙牌,編號分別為 1,2,…, N。每堆上有若干張,但紙牌總數必為 N 的倍數。可以在任一堆上取若於張紙牌,然后移動。
移牌規則為:在編號為 1 堆上取的紙牌,只能移到編號為 2 的堆上;在編號為 N 的堆上取的紙牌,只能移到編號為 N-1 的堆上;其他堆上取的紙牌,可以移到相鄰左邊或右邊的堆上。
現在要求找出一種移動方法,用最少的移動次數使每堆上紙牌數都一樣多。
例如 N=4,4 堆紙牌數分別為:
① 9 ② 8 ③ 17 ④ 6
移動3次可達到目的:
從 ③ 取 4 張牌放到 ④ (9 8 13 10) -> 從 ③ 取 3 張牌放到 ②(9 11 10 10)-> 從 ② 取 1 張牌放到①(10 10 10 10)。
先放代碼在分析吧,代碼比較短。
int main(){ int N; int pokers[MAX]; cin>>N; int total = 0; for (int i=0; i<N; i++) { cin>>pokers[i]; total+=pokers[i]; } int avg = total/N,times=0; for(int i=0;i<N;i++){ if(pokers[i]!=avg){ pokers[i+1] -= avg - pokers[i]; times++; } } cout<<times<<endl; }
可以看到最核心的那個循環的思想是這樣的:
從第一堆牌開始處理,如果第一堆牌整好是avg那么就放在一邊不管了。
如果第一堆牌不是avg,那么就要把第二堆牌(合法的移動只有從2移到1,這也是這個算法的精髓之處)移動幾張到第一堆,恰好使第一堆等於avg,從而只考慮第二堆開始到第N堆為止這些堆如何搞的子問題。然后依次遞歸下去。
這里的一個小技巧是認為牌數可以為負數,這樣才能繼續下去。綜上,這個步驟是合理的。但是看不出來是最優的。可見,貪心法確實是比較容易實現,因為比較符合人類直覺,但是不好證明。
再反過來看一下前面提到的幾點,可行性滿足,不可取消,每一次操作都是直接賦值,局部最優,當前情況下,只能從右往左移動,且貪心地想盡快讓第一堆滿足約束。
至於為什么是最優解,(最少的步驟),要看這個問題到底是不是具有貪心選擇性的。也就是看是不是全局最優解是由局部最優解產生的。對於這個事情,需要嚴格的數學證明才行。
http://www.zhihu.com/question/27883948 在知乎上問了這個問題。