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