博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
贪心算法 - 活动选择问题
阅读量:6480 次
发布时间:2019-06-23

本文共 2286 字,大约阅读时间需要 7 分钟。

前言:贪心算法也是用来解决最优化问题,将一个问题分成子问题,在现在子问题最优解的时,选择当前看起来是最优的解,期望通过所做的局部最优选择来产生一个全局最优解。书中先从活动选择问题来引入贪心算法,分别采用动态规划方法和贪心算法进行分析。本篇笔记给出活动选择问题的详细分析过程,并给出详细的实现代码进行测试验证。关于贪心算法的详细分析过程,下次在讨论。

1、活动选择问题描述

    有一个需要使用每个资源的n个活动组成的集合S= {a1,a2,···,an },资源每次只能由一个活动使用。每个活动ai都有一个开始时间si和结束时间fi,且 0≤si<fi<∞ 。一旦被选择后,活动ai就占据半开时间区间[si,fi)如果[si,fi]和[sj,fj]互不重叠,则称ai和aj两个活动是兼容的。该问题就是要找出一个由互相兼容的活动组成的最大子集。例如下图所示的活动集合S,其中各项活动按照结束时间单调递增排序。

从图中可以看出S中共有11个活动,最大的相互兼容的活动子集为:{a1,a4,a8,a11,}和{a2,a4,a9,a11}。

2、动态规划解决过程

(1)活动选择问题的最优子结构

定义子问题解空间Sij是S的子集,其中的每个获得都是互相兼容的。即每个活动都是在ai结束之后开始,且在aj开始之前结束。

为了方便讨论和后面的计算,添加两个虚构活动a0和an+1,其中f0=0,sn+1=∞。

结论:当i≥j时,Sij为空集。

如果活动按照结束时间单调递增排序,子问题空间被用来从Sij中选择最大兼容活动子集,其中0≤i<j≤n+1,所以其他的Sij都是空集。

最优子结构为:假设Sij的最优解Aij包含活动ak,则对Sik的解Aik和Skj的解Akj必定是最优的。

通过一个活动ak将问题分成两个子问题,下面的公式可以计算出Sij的解Aij

(2)一个递归解

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

当i≥j时,Sij必定为空集,否则Sij则需要根据上面提供的公式进行计算,如果找到一个ak,则Sij非空(此时满足fi≤sk且fk≤sj),找不到这样的ak,则Sij为空集。

c[i][j]的完整计算公式如下所示:

 

3、贪心算法解决过程

针对活动选择问题,认真分析可以得出以下定理:对于任意非空子问题Sij,设am是Sij中具有最早结束时间的活动,那么:

(1)活动am在Sij中的某最大兼容活动子集中被使用。

(2)子问题Sim为空,所以选择am将使子问题Smj为唯一可能非空的子问题。

有这个定理,就简化了问题,使得最优解中只使用一个子问题,在解决子问题Sij时,在Sij中选择最早结束时间的那个活动。

贪心算法自顶向下地解决每个问题,解决子问题Sij,先找到Sij中最早结束的活动am,然后将am添加到最优解活动集合中,再来解决子问题Smj

基于这种思想可以采用递归和迭代进行实现。递归实现过程如下所示:

void recursive_activity_selector(int *s,int* f,int i,int n,int *ret) { int *ptmp = ret; int m = i+1; //在Sin中寻找第一个结束的活动 while(m<=n && s[m] < f[i]) m = m+1; if(m<=n) { *ptmp++ = m; //添加到结果中 recursive_activity_selector(s,f,m,n,ptmp); } }

迭代实现过程如下:

void greedy_activity_selector(int *s,int *f,int *ret){  int i,m; *ret++ = 1; i =1; for(m=2;m<=N;m++) if(s[m] >= f[i]) { *ret++ = m; i=m; } }

完整C++代码:

#include
using namespace std;const int N=11; int ret[11]={ 0}; static int num=0; void recursive_activity_selector(int *s,int *f,int i,int n) { int m=i+1; while(m<=n&&f[i]>s[m]) m=m+1; if(m<=n) { ret[num++]=m; recursive_activity_selector(s,f,m,n); } } void greedy_activity_selector(int *s,int *f,int i,int n) { int m=i+1; while(m<=n) { if(f[i]<=s[m]) { ret[num++]=m; i=m; } m++; } } int main() { int s[N+1] = {-1,1,3,0,5,3,5,6,8,8,2,12}; int f[N+1] = {-1,4,5,6,7,8,9,10,11,12,13,14}; int i; recursive_activity_selector(s,f,0,N); cout<<"最大子集为: "<

运行结果:

转载地址:http://jpfuo.baihongyu.com/

你可能感兴趣的文章
二分法求平方根(Python实现)
查看>>
使用startActivityForResult方法(转)
查看>>
so在genymotation中错误问题
查看>>
Visual Studio 原生开发的10个调试技巧(二)
查看>>
Windows内核再次出现0Day漏洞 影响win2000到win10所有版本 反病毒软件恐成瞎子
查看>>
H3C品牌刀片系统强势首发
查看>>
【CSS系列】图像映射
查看>>
First blood
查看>>
java 冒泡排序和快速排序 实现
查看>>
SQL存储过程中的几个常见设定SET QUOTED_IDENTIFIER/NOCOUNT/XACT_ABORT ON/OFF
查看>>
Silverlight与Flash区别之一
查看>>
删除恢复Hadoop集群中的DataNode
查看>>
Silverlight 2动态创建矩形对象(附完整源代码)
查看>>
从京东技术演进看互联网企业的成长历程
查看>>
MFC ado+mysql+odbc技术分享
查看>>
js中让字符串中特定字符红色显示
查看>>
HttpClient4.5教程-第二章-连接管理
查看>>
redhat Nginx 安装
查看>>
oracle 配置监听
查看>>
上海访微软 详解Azure和S+S
查看>>