贪心算法——入门级讲解

特别说明:本文是小编基于罗勇军老师算法竞赛一书中贪心算法章节,对该部分内容进行的理解和总结。

一、基本概念

  所谓的贪心算法,就是把整个问题分成多个步骤,在每个步骤中都选取当前步骤的最优方案,直到所有步骤结束。在每一步都不考虑对后续步骤的影响,在后续步骤中也不再回头改变前面的选择。就是“走一步看一步”。
  贪心法看起来似乎不靠谱,因为局部最优不一定是全局最优。那么,是否有些规则使得局部最优能达到全局最优?

二、最少硬币问题(引入)

  某人带着3种面值的硬币去购物,有1元、2元、5元的,硬币数量不限,他需要支付M元,问:怎么支付使得硬币最少。思路:先拿5元,再拿2元,再拿1元。

package Practice;
import java.util.Scanner;

public class Main {
    final static int NUM=3;
    final static int[] Value={1,2,5};
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        int i,money;
        int[] ans=new int[NUM];//记录每种钱的数 ans[0]:1 ans[1]:2 ans[2]:5
        money=sc.nextInt();//输入钱数
        for(i=NUM-1;i>=0;i--){
            ans[i]=money/Value[i];
            money=money-ans[i]*Value[i];
        }
        for(i=NUM-1;i>=0;i--){
           System.out.println(Value[i]+"元硬币"+ans[i]);
        }
    }

  但是,在最少硬币问题中,如果稍微改一下参数,就不一定得到最优解,甚至在有解的情况下算不出答案。例如,硬币面值比较奇怪,是1、2、4、5、6元,支付9元,如果用贪心算法,答案6+2+1=9三个硬币,而最优只需4+5两个硬币。用3、5元硬币支付9元,用贪心法无解,而此时我们就需要用到动态规划这一算法(本文不对动态规划进行讲解)。

三、判断一个问题是否能用贪心法

  最优子结构性质。当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质,也称此问题满足最优性原理。也就是说,从局部最优能扩展到全局最优。

四、常见问题

(1)活动安排问题
给出节目时间表,如何安排看节目的顺序,能够看更多的节目
下面是航电的一道题,题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2037
航电的一道题
3种贪心策略:
1、最早开始时间
2、最早结束时间
3、用时最短

  我们很容易发现,第一种策略是错的,因为如果一个活动迟迟不结束,后面的活动无法进行。第三种策略也是错的。采用第二种策略。

解题步骤:
1)把n个活动按结束时间排序
2)选择第1个结束的活动,并删除(或跳过)与它时间相冲突的活动。
3)重复步骤2),直到活动为空。

package Practice;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc=new Scanner (System.in);
        int n;
        while (true){
            n=sc.nextInt();
            if(n==0)
                break;
            int[][] arr = new int[2][n];
            for(int i=0;i<n;i++){
                arr[0][i]=sc.nextInt();
                arr[1][i]=sc.nextInt();
            }
            for(int i=0;i<n;i++){
                for(int j=i+1;j<n;j++){
                    if(arr[1][i]>arr[1][j])
                    {
                        int temp=arr[0][i];
                        arr[0][i]=arr[0][j];
                        arr[0][j]=temp;
                        temp=arr[1][i];
                        arr[1][i]=arr[1][j];
                        arr[1][j]=temp;
                    }
                }
            }
            int s;
            int count=1;
            s=arr[1][0];
            for(int i=1;i<n;i++) {
                if (arr[0][i] >= s) {
                    count++;
                    s = arr[1][i];
                }
            }
            System.out.println(count);
        }
    }
}

分割线
(2)区间覆盖问题
  给定一个长度问n的区间,再给出m条线段的左端点和右端点,问最少用多少条线段可以将整个区间完全覆盖。(找尽量长的线段)

解题步骤:
1)把每个线段按照左端点递增排序
2)设已经覆盖的区间是[L,R],在剩下的线段中找出所有左端点小于等于R且右端点最大的线段,把这个线段加入到已覆盖区间里,并更新已覆盖区间的[L,R]值。
3)重复步骤2),直到区间全部覆盖。

分割线

(3)最优装载问题
  例题:有n种药水,浓度不同,把它们混合起来,得到浓度不大于w%的药水,问怎么混合才能得到体积最大的药水?注意一种药水要么全用,要么都不用,不能只取一部分。

解题思路:题目要求配置浓度不大于w%的药水,那么贪心的思路就是尽量找浓度小的药水。先对药水浓度从小到大排序,药水浓度不大于w%就加入,如果药水浓度大于w%,计算混合后的总浓度,不大于w%就加入,否则结束。
分割线
(4)多人接水问题
  有n个人排队到m个水龙头去接水,他们装满水桶的时间t1,t1……tn为整数且各不相同,应如何安排他们的接水顺序,才能是他们的总用时间最少,最少是多少?
解题思路:让打水耗时最短的人先接水。
分割线

五、视频讲解

基于本文的讲解视频

六、结语

  以上是小编整理和总结的一些内容,如有什么错误或遗漏,欢迎大家及时指出和补充。最后,用一句名言作为文章的结尾,送给每一个看这篇博客的人!

知识是一种快乐,而好奇则是知识的萌芽。 —— 培根

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值