贪心算法例题

简介

贪心算法(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;   

总结

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

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值