简介
贪心算法(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;
总结
我认为贪心算法其实是一种策略,对于某个问题使用某种策略去求得最优解。做题的时候,首先需要更具题目来判断,是否可以使用贪心算法来解决,判断依据就是:局部最优解是否能组成全局最优解决。如果能,就需要指定求局部最优解的策略,也就是选择的依据。是按照面值最大的先先选;还是按照速度最快的先走;亦或是按照性价比最高的先买?