贪心算法:活动选择问题

Posted 中学生编程与信息学竞赛自学

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了贪心算法:活动选择问题相关的知识,希望对你有一定的参考价值。

本课程是从少年编程网转载的课程,目标是向中学生详细介绍计算机比赛涉及的编程语言,数据结构和算法。编程学习最好使用计算机,请登陆 www.3dian14.org (免费注册,免费学习)。



我们继续回到上一堂课留下的课外习题:活动选择问题。活动选择问题是很常见的场景。例如各个部门共享一个会议室,利用该算法能使会议室安排尽量多的会议。


【问题】给你n个活动的开始时间和结束时间,从中选择你可以参与的活动,但是同一时间你只能参与一个活动,请找出你可以参与的最多活动数。


例如:考虑下面3个活动a1,a2和a3, 它们{开始时间点,结束时间点}分别为:

a1 {start=10,finish=20}

a2 {start=12,finish=25}

a3 {start=20,finish=30}


贪心算法直接在每一步选择当前看来最好的选择。在开始时,选择活动结束时间最早的那个活动,这样能够给其他活动尽可能的腾出多余的时间。而后每一步都在剩下的活动中选取,也遵循类似的原则。由于获取已经按照结束时间排序好,所以这里第一个选择的活动就是a0,由于a0于时间20结束,马上再找一个活动,只有a2可以选择,a2结束之后再也没有活动可选了。因此得到答案:最多可以参加两个活动(a0,a2)。



贪心算法(4):活动选择问题

算法分析和设计


现在请你设计一种贪心算法解决类似活动选择问题。


我们设计下列贪心算法的贪心策略:选择其余活动中完成时间最短的下一个活动,并且开始时间大于或等于先前所选活动的结束时间。 我们可以根据他们的完成时间对活动进行排序,以便我们始终将下一个活动视为最小完成时间活动。


算法描述如下


1)根据完成时间对活动进行排序

2)从排序的数组中选择第一个活动并输出

3)对已排序数组中的剩余活动执行以下操作:

     如果此活动的开始时间大于或等于先前所选活动的结束时间,则选择此活动并输出;


回到上堂课的习题:考虑下面6个活动,请找出你可以参与的最多活动数

 {start=0,finish=6}

 {start=1,finish=2}

 {start=3,finish=4}

 {start=5,finish=7}

 {start=5,finish=9}

 {start=8,finish=9}


首先按结束时间对它们进行排序:

a0{start=1,finish=2}

a1{start=3,finish=4}

a2{start=0,finish=6}

a3{start=5,finish=7}

a4{start=8,finish=9}

a5{start=5,finish=9}


然后按2)和3)两个步骤进行活动选择。选择过程如下图所示:



贪心算法(4):活动选择问题

此这种情形下,一个人最多只能参与4个活动:分别是从1开始、3开始、5开始以及8开始的4个活动。你答对了吗?



算法的正确性证明


让给定的一组活动为S = {1,2,3,.. n},并且已按活动结束时间对所有活动进行排序。 贪心策略总是选择【活动1】。为什么【活动1】始终是最佳解决方案之一? 


假设存在除了【活动1】以外的另一个最佳解决方案S,并且S选择的第一个活动是【活动k】,而不是【活动1】。现在我们来说明,把【活动k】替换成【活动1】,得到的新方案S‘,{B-{k}}U{1},必定仍然是一个最佳解决方案,说明如下:因为中的活动是独立的,而在排序队列中,【活动1】在所有活动中具有最小的结束时间,因为k不等于1,【活动k】的完成时间必定是大于等与【活动1】的完成时间,因此把【活动k】换成【活动1】后的新方案S‘必定也是最佳解决方案。


算法实现


在以下C/C++代码实现中,假设活动已根据其完成时间进行了排序。


#include<stdio.h> 

//  n   -->  活动个数

//  s[] -->  数组保存所有活动的开始时间 

//  f[] -->  数组保存所有活动的结束时间 

void printMaxActivities(int s[], int f[], int n) 

    int i, j; 

  

    printf ("选择以下的活动\n"); 

  

    // 第一个活动总是选中 

    i = 0; 

    printf("%d ", i); 

  

    // 依次检查余下的活动

    for (j = 1; j < n; j++) 

    { 


     //如果某活动在之前选择的活动结束之后开始

      if (s[j] >= f[i]) 

      { 

          printf ("%d ", j); 

          i = j; 

      } 

    } 

  

//主程序

int main() 

    int s[] =  {1, 3, 0, 5, 8, 5}; 

    int f[] =  {2, 4, 6, 7, 9, 9}; 

    int n = sizeof(s)/sizeof(s[0]); 

    printMaxActivities(s, f, n); 

    return 0; 


注意:若是finish数组没有排序,需要先对它进行排序。作为课外练习,请你在上述代码基础上加上排序部分,完成对finish数组的排序




以上是关于贪心算法:活动选择问题的主要内容,如果未能解决你的问题,请参考以下文章

贪心算法:活动选择问题

活动安排问题-贪心算法

活动安排问题-贪心算法

算法导论笔记——第十六章 贪心算法

数据结构与算法笔记(十七)—— 贪心算法及经典案例(找零问题背包问题拼接最大数字问题活动选择问题)

数据结构与算法笔记(十七)—— 贪心算法及经典案例(找零问题背包问题拼接最大数字问题活动选择问题)