【算法學習筆記】25.貪心法 均分紙牌問題的分析


貪心法:

貪⼼算法(又稱貪婪算法)是指,在對問題求解時,總是做出在當前看來是最好的選擇。也就是說,不從整體最優上加以考慮,他所做出的僅是在某種意義上的局部最優解。

貪心算法不是對所有問題都能得到整體最優解,關鍵是貪⼼心策略的選擇,選擇的貪⼼策略必須具備⽆后效性,即某個狀態以后的過程不會影響以前的狀態,只與當前狀態有關。

太概念化了。總結起來三點:

可行性:必須滿足問題的約束

局部最優:當前步驟中所有可行的選擇里最佳的局部選擇。

不可取消:選擇一旦做出,后面的步驟就無法改變。

問題要具有貪心選擇性:問題的最優解可以通過一系列的局部最優選擇來達到。(最重要的一步,決定這個問題是否可以用貪心法來解決,此處的解決特指找到最優解)。

最優子結構性質:指一個問題的最優解一定要包含子問題的最優解。

貪心和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 在知乎上問了這個問題。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM