貪心算法例題

簡介

貪心算法(greedy)分階段地工作,在每一個階段都可以認爲所做的決定是最好的,而不用考慮後果。這就意味着得到的是局部最優解決方案,當算法結束時,如果一個一個局部最優解能組成全局最優解決,那麼就說明這個算法是正確的;如果不是,則此算法的到的結果就是一個次優解。因此,如果使用貪婪算法得到問題的最優解,那麼問題就必須滿足一定的條件。

找零錢問題

這個問題在我們的日常生活中非常普遍了。假設1元、2元、5元、10元、20元、50元、100元的紙幣。現在要用這些錢來支付K元,至少要用多少張紙幣?用貪心算法的思想,很顯然,每一步儘可能用面值大的紙幣即可。在日常生活中我們自然而然也是這麼做的。在程序中已經事先將Value按照從大到小的順序排好,第一次我們先取面值最大的,然後取次大的。。。。。。每次取都是從面額最大的開始取,獲得局部最優解,而最終獲得全局最優解。代碼如下:

public class Greedy {

    public List<Integer> change(int money,int[] limit){
        //Arrays.sort(limit);
        List<Integer> result = new ArrayList<>();
        int left = money;
        int i = 0;
        while(left>0){
            if((left - limit[i])<0){
                i++;
            }else {
                left -= limit[i];
                result.add(limit[i]);
            }
        }
        return result;
    } 

    public static void main(String[] args) {
        Greedy greedy = new Greedy();
        int money = 96;
        int[] limit = {50,20,10,5,1};
        List<Integer> change = greedy.change(money, limit);
        for (Integer integer : change) {
            System.out.println(integer);
        }
    }
}

/*
50
20
20
5
1
*
/

揹包問題

我們知道三種最基本的揹包問題:零一揹包,部分揹包,完全揹包。很容易證明,揹包問題不能使用貪心算法。然而我們考慮這樣一種揹包問題:在選擇物品i裝入揹包時,可以選擇物品的一部分,而不一定要全部裝入揹包。這時便可以使用貪心算法求解了。計算每種物品的單位重量價值(性價比)作爲貪心選擇的依據指標,選擇單位重量價值最高的物品,將儘可能多的該物品裝入揹包,依此策略一直地進行下去,直到揹包裝滿爲止。

class Goods{
   int id;// 物體的序號
   int w;// 物體的重量
   int p;// 物體的價值
}


List<Goods> commonPackage( int[] w, int[] p, int m ){
    List<Goods> goods = new ArrayList<>();
    for ( int i=0; i<w.length; i++ ) {
        Goodss.add(new Goods(w[i],p[i]));
    }
    // 對性價比從高到低排序
    Collections.sort(Goodss, new Comaprator<Goods>(){
        int compare(Goods b1,Goods b2){
            return b2.p/b2.w-b1.p/b1.w;
        }
    });
    // 剩餘重量
    int rest = m;
    int i;
    // 存放結果
    List<Goods> results = new ArrayList<>();
    for(i=0; i<Goodss.size(); i++){
        if ( rest<Goodss.get(i).w )
            break;
        Goods curGoods = Goodss.get(i);
        results.add(curGoods);
        rest -= curGoods.w;
    }
    // 計算最後一個物體能放入的部分
    Goods lastGoods = Goodss.get(i);
    results.add(new Goods(lastGoods.id,rest,(lastGoods.p*rest/lastGoods.w));
}

小船過河問題

只有一艘船,能乘2人,船的運行速度爲2人中較慢一人的速度,過去後還需一個人把船劃回來,問把n個人運到對岸,最少需要多久。局部最優解是這樣的:將最快的和次快的綁定在一起,然後最快的划船回來;再將最快的和次次塊的一起,然後最快的回來。這樣我們可以知道,這些局部最優解組成了全局最優解。證明如下:假設序號爲i的人划船時間爲ti,那麼總時間=划船回來的時間+划船過去的時間,也就是t1+t2+。。。+tn+划船回來的時間。划船回來的總時間最短爲n*最快的人的時間。
這裏我寫一下僞代碼:

//從時間上從快到慢排序
Arrays.sort(t[n]);
for i=1 to n-1;
    //計算去的時間
    sum + = t[i];
    //計算回來的時間
    back = (n-1)*t[0];
totalTime = sum+back;   

總結

我認爲貪心算法其實是一種策略,對於某個問題使用某種策略去求得最優解。做題的時候,首先需要更具題目來判斷,是否可以使用貪心算法來解決,判斷依據就是:局部最優解是否能組成全局最優解決。如果能,就需要指定求局部最優解的策略,也就是選擇的依據。是按照面值最大的先先選;還是按照速度最快的先走;亦或是按照性價比最高的先買?

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章