贪心算法

1、前言

求解最优化问题的算法通常会经历一系列步骤,在每个步骤都会面临多种选择,而许多最优化问题并不需要计算每个选择,它的选择非常明确。

贪心算法就是这样的算法,它在每一步中都做出当时看起来最佳的选择,它总是做出局部最优选择从而实现最优解

2、贪心算法原理

贪心算法的步骤:

  • 确定总是的最优子结构
  • 设计一个递归算法
  • 证明如果我们做出一个贪心选择,则只剩下一个子总是
  • 证明贪心选择总是安全的
  • 设计一个递归算法实现贪心策略
  • 将递归算法转换为迭代算法

什么时候才能使用贪心算法的呢?书中给出了贪心算法的两个性质,只有最优化问题满足这些性质,就可采用贪心算法解决问题。

(1)贪心选择性质:一个全局最优解可以通过最优解(贪心)选择来达到。即:当考虑做选择时,只考虑对当前问题最佳的选择而不考虑子问题的结果。

(2)最优子结构:问题的一个最优解包含了其子问题的最优解。

贪心算法一般来说都能用动态规划解决,因为动态规划需要的条件,贪心算法都满足,而贪心算法像是简化版的动态规划,它不需要计算每个选择。贪心算法只需要选择一个当下最优选择即可。

3、活动选择问题

假定由n个活动组成的集合S= {a1,a2,···,an },这些活动使用同一个资源。每个活动ai都有一个开始时间si和结束时间fi,且 0≤si<fi<∞ 。被选择后,活动ai就占据半开时间区间[si,fi)。如果[si,fi]和[sj,fj]互不重叠,则称ai和aj两个活动是兼容的。

该问题就是要找出一个由互相兼容的活动组成的最大子集。例如下图所示的活动集合S,其中各项活动按照结束时间单调递增排序。

根据上图可知,最大子集为1、4、8、11。

4、动态规划解题

最大活动子集问题是否满足最优子结构呢?仔细思考发现此问题类似于钢条切割问题,使用复制剪切法,将最优解分为两个规模相当的子问题,那么子问题必然也是最优解。

具有最优子结构,那么它的状态方程式是什么呢?

定义子问题解空间Sij是S的子集,其中每个活动都是在ai结束之后开始,且在aj开始之前结束。当 i>=j 时,Sij为空集。

设c[i][j]为Sij中最大兼容子集中的活动数目,当Sij为空集时,c[i][j]=0;当Sij非空时,若ak在Sij的最大兼容子集中被使用,则则问题Sik和Skj的最大兼容子集也被使用,故可得到c[i][j] = c[i][k]+c[k][j]+1。

由此可知状态方程式为:

  /**
 * 动态规划计算最大活动数。
 * 此问题满足最优子问题条件,而且可推导公式为:
 * max = Math.max(max, c[i][k] + c[k][j] + 1);
 * 类似钢条切割,一定是中间某点分割,两边都是最优选
 */
public static int dpSelect(){
    int[] st = {1,3,0,5,3,5,6,8,8,2,12}; 
    int[] ft = {4,5,6,7,8,9,10,11,12,13,14};
    int length = st.length;
    int[][] c = new int[length][length];
    int[][] ret = new int[length][length];
    
    for (int i = 0; i < length; i++) {
        for (int j = 1; j < length; j++) {
            if (i == j) {
                c[i][j] = 0;
            }else {
                int max = 0;
                for (int k = i+1; k < j; k++) {
                    if (st[k] >= ft[i] && ft[k] <= st[j]) {
                        max = Math.max(max, c[i][k] + c[k][j] + 1);
                        ret[i][j] = k;
                    }
                }
                /*
                 * max等于0,即没有合适的k。
                 * 如果集合的首位和末位时间合适,所以会有两个活动
                 * 如果集合的首位和末位时间不合适,所以会有一个活动
                 */
                if (max == 0) {
                    if (st[j] >= ft[i]) {
                        max += 2;
                    }else {
                        max += 1;
                    }
                }
                c[i][j] = max;
            }
        }
    }
    System.out.println(c[0][10]);
    System.out.println("----------");
    printResult(st, ft, 0, 10, ret);
    return c[0][length-1];
}

printResult方法,根据ret数组中的值,计算一共选择了哪些活动。

  public static void printResult(int[] st, int[] ft, int i, int j, int[][] ret){
    int r = ret[i][j];
    if (r > 0) {
        System.out.println(r);
    }
    while (r > 0 && r < st.length && (r=ret[i][r]) > 0) {
        System.out.println(r);
    }
    if (st[j] >= ft[i]) {
        System.out.println(i);
        System.out.println(j);
    }else {
        System.out.println(i);
    }
}

5、贪心算法

贪心算法是动态规划的简化版本。

此问题使用贪心算法非常简单,因为活动结束时间已经按照从小到大顺序排列了,为了实现最大子集的目标,所以第1个活动一定要入选,因为第1个活动最早结束,能够给出最多的时间让其它活动入选。

接下来遍历每一个活动,只要新活动的开始时间大于或等于已经入选的活动,则此活动也可以入选。因为结束时间从小到大排列的原因,新入选的活动一定是最优的。

代码如下:

/**
 * 贪心算法计算最大活动数
 */
public static void greedSelect(){
    int[] st = {1,3,0,5,3,5,6,8,8,2,12}; 
    int[] ft = {4,5,6,7,8,9,10,11,12,13,14};
    
    int length = st.length;
    int current = 0;
    System.out.println("0 is selected");
    for (int i = 1; i < length; i++) {
        //因为活动结束时间已经按从小到大顺序排列,那么只要活动开始时间大于current结束时间
        //那么此活动一定会是最优的。有可能其它活动开始时间也满足要求,但他们的结束时间一定比i大。
        if (st[i] >= ft[current]) {
            current = i;
            System.out.println(current + " is selected");
        }
    }
}

ps:所有代码均已上传至本人的github,欢迎访问

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,290评论 6 491
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,107评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,872评论 0 347
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,415评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,453评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,784评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,927评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,691评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,137评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,472评论 2 326
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,622评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,289评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,887评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,741评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,977评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,316评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,490评论 2 348

推荐阅读更多精彩内容

  • 目录 1.贪心算法步骤 2.两个关键要素 3.两种背包问题3.1 0-1背包问题(适用于动态规划,不满足贪心选择性...
    王侦阅读 4,913评论 2 3
  • 贪心算法 当具有最优子结构性质的时候,可以使用动态规划算法也可以使用贪心算法 最优子结构性质、贪心选择性质 虽然贪...
    冰源阅读 1,012评论 0 0
  • 可用贪心算法解决的几个基本问题 关键:看问题有没有贪心选择性质和最优子结构性质。有些问题看似是可以用贪心算法,但是...
    碧影江白阅读 6,194评论 1 2
  • 非准程序员请绕道,这篇文章不是你想看的。(而且很长,虽然满满的干货) 写下这个字的时间点是23:53,是时候关掉电...
    谢培阳阅读 1,216评论 1 5
  • 昨日屋内不慎入三蝇,甚恶。驱之,两走,死一。
    飒飒的帅气昵称阅读 163评论 0 0