贪心算法例题

来源:互联网 发布:数据精灵稳定版源 编辑:程序博客网 时间:2024/06/16 11:09

简介

贪心算法(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);        }    }}/*50202051*/

背包问题

我们知道三种最基本的背包问题:零一背包,部分背包,完全背包。很容易证明,背包问题不能使用贪心算法。然而我们考虑这样一种背包问题:在选择物品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;   

总结

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