贪心算法浅析
C语言贪心算法分析

这是《算法导论》上的例子,也是一个非常经典的问题。有 n 个需要在同一 天使用同一个教室的活动 a1,a2,…,an,教室同一时刻只能由一个活动使用。每 个活动 ai 都有一个开始时间 si 和结束时间 fi 。一旦被选择后,活动 ai 就占据半 开时间区间[si,fi)。如果[si,fi]和[sj,fj]互不重叠,ai 和 aj 两个活动就可以被安排在 这一天。该问题就是要安排这些活动使得尽量多的活动能不冲突的举行。例如下 图所示的活动集合 S,其中各项活动按照结束时间单调递增排序。
五、贪心选择性质
所谓贪心选择性质是指所求问题的整体最优解可以通过一系列局部最优的
选择,换句话说,当考虑做何种选择的时候,我们只考虑对当前问题最佳的选择 而不考虑子问题的结果。这是贪心算法可行的第一个基本要素。贪心算法以迭代 的方式作出相继的贪心选择,每作一次贪心选择就将所求问题简化为规模更小的 子问题。对于一个具体问题,要确定它是否具有贪心选择性质,必须证明每一步 所作的贪心选择最终导致问题的整体最优解。
七、例题分析
如果大家比较了解动态规划,就会发现它们之间的相似之处。最优解问题大 部分都可以拆分成一个个的子问题,把解空间的遍历视作对子问题树的遍历,则 以某种形式对树整个的遍历一遍就可以求出最优解,大部分情况下这是不可行的。 贪心算法和动态规划本质上是对子问题树的一种修剪,两种算法要求问题都具有 的一个性质就是子问题最优性(组成最优解的每一个子问题的解,对于这个子问 题本身肯定也是最优的)。动态规划方法代表了这一类问题的一般解法,我们自 底向上构造子问题的解,对每一个子树的根,求出下面每一个叶子的值,并且以 其中的最优值作为自身的值,其它的值舍弃。而贪心算法是动态规划方法的一个 特例,可以证明每一个子树的根的值不取决于下面叶子的值,而只取决于当前问 题的状况。换句话说,不需要知道一个节点所有子树的情况,就可以求出这个节 点的值。由于贪心算法的这个特性,它对解空间树的遍历不需要自底向上,而只 需要自根开始,选择最优的路,一直走到底就可以了。
贪心算法实例分析

贪心算法实例分析贪心算法,是一种常见的解决问题的方法,尤其在最优化问题中得到了广泛的应用。
简单来说,贪心算法是建立在选取局部最优解的基础上,通过不断的贪心选择,达到全局最优解的目的。
本文将通过两个实例,介绍使用贪心算法的思路和实现方法。
实例一:零钱兑换问题假设有 n 种不同面值的硬币,每种面值的硬币数量无限,现在需要兑换一个目标值amount 元,求兑换所需的最少硬币数。
例如:有2元、5元、10元、20元、50元、100元六种面值的硬币,需要兑换52元,则兑换最少需要三枚硬币:两枚20元硬币和一枚10元硬币。
思路分析:对于这个问题,我们可以把它看成选择一定数量的硬币使其面值之和等于 amount,且硬币数量最少。
为了使硬币数量最少,我们每次都选取当前面值最大并且小于剩余兑换金额的硬币。
这相当于在每个阶段选择局部最优解,最终得到的就是全局最优解。
实现方法:1. 排序:将硬币面额按从大到小的顺序排列。
2. 贪心选择:从面额最大的硬币开始选取,直到兑换金额为 0 或全部面额都被选择过。
代码示例:```pythondef coinChange(coins, amount):coins.sort(reverse=True) # 排序res = 0for coin in coins:while amount >= coin: # 贪心选择amount -= coinres += 1return res if amount == 0 else -1```实例二:区间调度问题假设有 n 个区间 [start, end],现在需要选出最多的区间,使得它们没有重叠。
例如:有四个区间[1,3]、[2,4]、[3,6]、[5,7],选出的最多区间数是 3,即选出[1,3]、[3,6]、[5,7]这三个区间。
思路分析:这个问题可以看成排序和贪心选择问题的复合。
首先,我们将所有区间按照 end 非递减的顺序排序。
然后从第一个区间开始,依次选取与当前区间不重叠且start 大于等于当前区间的所有区间,直到所有区间都被检查完。
浅谈贪心算法

贪心算法贪心算法是一种用来求最优解的算法,主要思想是将问题分为数个步骤,用一个贪心标准逐步求出每一步骤的最优解,最终产生全局最优解。
贪心算法的优点是效率高,缺点是最终产生的解不一定是最优解。
1.均分纸牌(NOIP2002提高组第1题)问题描述:有 N 堆纸牌,编号分别为 1,2,…, N。
每堆上有若干张,但纸牌总数必为 N 的倍数。
可以在任一堆上取若干张纸牌,然后移动。
移牌规则为:在编号为 1 堆上取的纸牌,只能移到编号为 2 的堆上;在编号为 N 的堆上取的纸牌,只能移到编号为 N-1 的堆上;其他堆上取的纸牌,可以移到相邻左边或右边的堆上。
现在要求用最少的移动次数S使每堆上纸牌数都一样多。
输入格式:Na[1], a[2], …, a[N]输出格式:S算法分析:从第1堆到第N堆纸牌,按从左到右的顺序,若第i堆纸牌数a[i]大于平均数k,则将多出的纸牌移到第i+1堆上;若a[i]小于k,则将缺少的纸牌数移到第i+1堆上(缺少的纸牌数可由右侧传递给左侧,次数相同)。
例:n=4 a[1..4] = 4 6 10 84 ——〉 6 ——〉 10 ——〉 8-3 -4 -1(等于右给左2)(右给左4)(右给左1)源程序:Vara: Array[1..100]Of Integer;i, n, t, s: Integer;BeginReadLn(n);For i := 1 To n Do Read(a[i]);For i := 1 To n Do t := t + a[i];t := t Div n; {求平均数}For i := 1 To n-1 DoIf a[i] <> t ThenBeginInc(s); {计数}a[i+1] := a[i+1] + (a[i] - t);End;WriteLn(s);End.在用贪心算法解题时,需要注意两方面,一是问题是否适用于贪心算法,二是应选用怎样的贪心标准。
贪心算法——精选推荐

贪⼼算法1、如何理解贪⼼算法贪⼼算法的思想是:每次都做出当前最优的选择,通过多步选择得出最终的最优解。
它适合解决上⼀步的选择不会影响下⼀步的选择的问题。
如果上⼀步的选择会影响下⼀步的选择,则使⽤贪⼼算法不⼀定能求出最优解。
1.1 能够使⽤贪⼼算法求解的问题举例问题:假如我们有⼀个能够容纳100Kg物品的袋⼦,可以装各种物品,不管物品的体积。
现在我们有5种⾖⼦,每种⾖⼦的总重量和总价值各不相同。
那如何往背包⾥⾯装这些⾖⼦,使得最后背包⾥物品的总价值最⼤呢?我们⼀眼就能知道这个问题的解法,先求出每种⾖⼦的单价,然后从单价⾼的⾖⼦开始装,装完单价⾼的,再去装次⾼的,直到袋⼦装满为⽌。
这个问题就适合⽤贪⼼算法来解,实际上上⾯的做法体现的就是贪⼼算法的思想(每次都做出当前最优的选择)。
这⾥第⼀步选择装单价最⾼的⾖⼦之后,不会影响第⼆步去选择装次⾼的⾖⼦的选择,同样第⼆步也不会影响第三步去选择单价排名第三的⾖⼦。
1.2 不能使⽤贪⼼算法来求解的问题举例问题:假如我们要在⼀个有权图中,从顶点S开始,找⼀条到顶点T的最短路径。
贪⼼算法的解决思路是,每次都选择⼀条根当前顶点相连的权最⼩的边(每次都做出当前最优的选择),直到找到顶点T。
按照这种思路,我们求出的最短路径是S->A->E->T,路径长度1+4+4=9;⽽实际的最短路径是S->B->D->T,路径长度2+2+2=6 。
为什么这⾥贪⼼算法得不到最优解呢?我们第⼀步从S->A和第⼀步从S->B,下⼀步⾯对的顶点和边是不⼀样的。
也就是我们前⾯的选择会影响后⾯的选择,所以得不出最优解。
⽐如:钱币找零'''假设现在市⾯上有 6 种不同⾯值的硬币,各硬币的⾯值分别为 5 分、1 ⾓、2 ⾓、5 ⾓、1 元、2 元,要找零 10.5 元,求出最少硬币的数量。
'''def getChange(coins, amount):coins.sort();# 从⾯值最⼤的硬币开始遍历i = len(coins)-1while i >= 0:if amount >= coins[i]:n = int(amount // coins[i])change = n * coins[i]amount -= changeprint (n, coins[i])i -= 1getChange([0.05,0.1,0.2,0.5,1.0,2.0], 10.5)再⽐如:使⽤贪⼼算法实现哈夫曼编码3.1 什么是哈夫曼编码哈夫曼编码是⼀种⼗分有效的编码⽅法,⼴泛应⽤于数据压缩中,其压缩率通常在20%~90%之间。
贪心算法详解

贪心算法详解贪心算法思想:顾名思义,贪心算法总是作出在当前看来最好的选择。
也就是说贪心算法并不从整体最优考虑,它所作出的选择只是在某种意义上的局部最优选择。
当然,希望贪心算法得到的最终结果也是整体最优的。
虽然贪心算法不能对所有问题都得到整体最优解,但对许多问题它能产生整体最优解。
如单源最短路经问题,最小生成树问题等。
在一些情况下,即使贪心算法不能得到整体最优解,其最终结果却是最优解的很好近似。
贪心算法的基本要素:1.贪心选择性质。
所谓贪心选择性质是指所求问题的整体最优解可以通过一系列局部最优的选择,即贪心选择来达到。
这是贪心算法可行的第一个基本要素,也是贪心算法与动态规划算法的主要区别。
动态规划算法通常以自底向上的方式解各子问题,而贪心算法则通常以自顶向下的方式进行,以迭代的方式作出相继的贪心选择,每作一次贪心选择就将所求问题简化为规模更小的子问题。
对于一个具体问题,要确定它是否具有贪心选择性质,必须证明每一步所作的贪心选择最终导致问题的整体最优解。
2. 当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。
问题的最优子结构性质是该问题可用动态规划算法或贪心算法求解的关键特征。
贪心算法的基本思路:从问题的某一个初始解出发逐步逼近给定的目标,以尽可能快的地求得更好的解。
当达到算法中的某一步不能再继续前进时,算法停止。
该算法存在问题:1. 不能保证求得的最后解是最佳的;2. 不能用来求最大或最小解问题;3. 只能求满足某些约束条件的可行解的范围。
实现该算法的过程:从问题的某一初始解出发;while 能朝给定总目标前进一步do求出可行解的一个解元素;由所有解元素组合成问题的一个可行解;用背包问题来介绍贪心算法:背包问题:有一个背包,背包容量是M=150。
有7个物品,物品可以分割成任意大小。
要求尽可能让装入背包中的物品总价值最大,但不能超过总容量。
物品A B C D E F G重量35 30 60 50 40 10 25价值10 40 30 50 35 40 30分析如下目标函数:∑pi最大约束条件是装入的物品总重量不超过背包容量:∑wi<=M( M=150)。
程序设计中的贪心算法研究

程序设计中的贪心算法研究随着计算机的日益普及,程序设计也成为了许多人的热门选择之一。
而在程序设计中,贪心算法也是一种非常重要的算法。
通过本文,我们将深入探讨贪心算法在程序设计中的应用及其研究。
一、贪心算法的基本思想贪心算法又称贪心策略,是一种在每一步选择中都采取当前状态下最优的选择,以求得全局最优解的算法思想。
其基本思想是:1. 建立数学模型来描述问题。
2. 把求解的问题分成若干个子问题。
3. 对每一个子问题求解,得到子问题的局部最优解。
4. 把子问题的局部最优解合成原问题的一个解。
例如,求解跳跳糖果问题,每次可以选择跳一格或者跳两格,而目标是要跳到终点。
那么贪心的策略就是每次都跳两格,只要还能跳两格就一直跳,直到跳不动了就跳一格。
这样可以最大程度地减少跳的次数,从而得到最优解。
二、贪心算法的应用在实际程序设计中,贪心算法被广泛应用。
其主要应用于以下几个方面:1. 最优化问题:贪心算法可以用于求解最小生成树、最短路径、背包问题等求最优解的问题。
2. 搜索问题:贪心算法可以用于广度优先搜索和深度优先搜索的启发式函数,来提高搜索效率。
3. 规划问题:贪心算法可以用于许多规划问题,如任务调度、资源分配等,来简化问题模型。
四、贪心算法的特点相比于其他算法,贪心算法有以下几个突出的特点:1. 算法简单:贪心算法不需要对问题进行复杂的分析和求解,只需要按照贪心策略分步求解即可。
2. 实现简单:贪心算法可以使用常规的循环和判断语句,实现难度较低。
3. 时间复杂度较低:贪心算法通常具有较低的时间复杂度,适用于大规模数据的求解。
4. 无法保证最优解:由于是依据当前状态下最优的选择,贪心算法无法保证全局最优解,存在求解错误的风险。
五、贪心算法的优化由于贪心算法存在求解错误的风险,因此需要在算法设计中考虑到这一点,并采取相应的优化措施来提高算法的准确性。
其中,常见的优化措施有:1. 排序:对问题中的元素按照某个关键字进行排序,以优先选择一些重要的元素。
贪心算法局限性分析及改进思路

贪心算法局限性分析及改进思路贪心算法是一种高效的算法设计思想,常用于求解最优化问题。
然而,贪心算法在某些情况下存在一定的局限性,导致无法得到全局最优解。
本文将对贪心算法的局限性进行分析,并提出一些改进思路。
一、贪心算法的局限性贪心算法在每个阶段都做出局部最优选择,以期望最终得到全局最优解。
然而,这种局部最优选择并不一定能够导致全局最优解,因此贪心算法存在以下局限性:1. 依赖于局部最优选择:贪心算法的核心在于对每个阶段的选择作出最优判断。
然而,如果在某个阶段出现局部最优选择,并不代表这个选择也能够推导出全局最优解。
因此,贪心算法容易陷入局部最优解,无法找到全局最优解。
2. 不能回退:贪心算法做出一次选择后,无法回退,因此无法纠正之前的选择,这可能会导致在后续的选择中出现问题。
特别是在涉及到排列组合或者约束条件的情况下,贪心算法无法回退就限制了其能够找到全局最优解的能力。
3. 问题依赖于子问题的解决方案:对于一些问题,贪心算法的有效性依赖于子问题的解决方案。
如果子问题的解决方案不能够产生全局最优解,并不能保证贪心算法能够得到全局最优解。
二、贪心算法的改进思路针对贪心算法的局限性,可以从以下几个方面对其进行改进,以期望提高算法的准确性和效率:1. 局部最优选择的验证:为了避免贪心算法陷入局部最优解,可以在每次做出选择后,对其进行验证。
验证的方法可以是通过暴力搜索,尝试其他的可能选择,以确保所做的选择能够推导出全局最优解。
2. 引入回溯机制:为了克服贪心算法无法回退的问题,可以引入回溯机制。
即在做出每次选择后,保存当前选择的状态,并在后续的选择中进行回溯,以便进行其他选择。
这样可以使算法具有更大的灵活性,能够找到更优的解。
3. 考虑约束条件和限制因素:有些问题的解决需要考虑到约束条件和限制因素。
在设计贪心算法时,可以结合这些约束条件和限制因素,将其融入到选择过程中,以保证算法能够找到满足约束条件的最优解。
易语言 贪心算法

易语言贪心算法贪心算法(Greedy Algorithm)是一种常用的算法思想,它在每一步选择中都采取当前状态下最优的选择,从而希望最终能够获得全局最优解。
本文将介绍贪心算法的基本概念、特点以及应用,并通过一些具体的例子来进一步说明其实现过程和效果。
一、贪心算法的基本概念贪心算法是一种基于贪心策略的求解问题的方法。
它通过不断地做出局部最优选择,来达到全局最优的目标。
贪心算法的基本思想是每一步都选择当前状态下最优的解决方案,而不考虑其对后续步骤的影响。
这种局部最优的选择最终希望能够达到全局最优的结果。
二、贪心算法的特点1. 简单易实现:贪心算法的实现相对简单,不需要复杂的数据结构和算法;同时,贪心算法的思想也较为直观,容易理解和应用。
2. 效率高:相比于其他算法,贪心算法通常具有较高的执行效率,时间复杂度较低。
3. 局限性:贪心算法只关注当前的最优解,而不考虑其对后续步骤的影响,因此可能会得到次优解或不正确的解。
三、贪心算法的应用贪心算法在实际问题中有着广泛的应用。
下面通过几个具体的例子来说明贪心算法的应用过程。
1. 找零问题:假设有一些零钱,如1元、5元、10元、20元、50元和100元,要用最少的零钱凑出一个给定的金额。
贪心算法可以选择面值最大的零钱来凑,然后再选择次大面值的零钱,依次类推,直到凑出给定金额为止。
2. 区间覆盖问题:假设有一些区间,需要选择尽可能少的区间来覆盖给定的目标区间。
贪心算法可以选择结束时间最早的区间,然后排除与该区间重叠的其他区间,依次类推,直到覆盖所有目标区间。
3. 集合覆盖问题:假设有一些需要覆盖的元素,以及一些集合,每个集合包含一些元素,需要选择尽可能少的集合来覆盖所有元素。
贪心算法可以选择包含最多未覆盖元素的集合,然后排除已覆盖的元素,依次类推,直到覆盖所有元素。
四、贪心算法的实现过程贪心算法的实现过程通常包括以下几个步骤:1. 确定问题的最优子结构性质。
2. 建立数学模型来描述问题。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
贪心算法浅析摘要:本文讲述了贪心算法的基本思路及实现过程,贪心算法的特点、存在的问题以及应用。
并通过贪心算法的特点举例列出了几个经典问题,通过对问题的探讨和研究,对贪心算法有了更加深入的了解。
关键词:贪心算法;最优解;最优子结构问题;删数问题;活动安排问题贪心算法的基本思路及实现过程1贪心的基本思想用局部解构造全局解,即从问题的某一个初始解逐步逼近给定的目标,以尽可能快地求得更好的解。
当某个算法中的某一步不能再继续前进时,算法停止。
贪心算法思想的本质就是分治,或者说:分治是贪心的基础。
每次都形成局部最优解,换一种方法说,就是每次都处理出一个最好的方案。
利用贪心策略解题,需要解决两个问题:(1)该题是否适合于用贪心策略求解;(2)如何选择贪心标准,以得到问题的最优/较优解。
2贪心算法的实现过程(1)应用同一规则F,将原问题变为一个相似的、但规模更小的子问题;(2)从问题的某一初始解出发:While(能朝给定目标前进一步)求出可行解的一个解元素;(3)由所有解元素组合成问题的一个可行解。
贪心算法的特点贪心算法的最大特点就是快,通常是线性二次式,不需要多少额外的内存。
一般二次方级的存储要浪费额外的空间,而且那些空间经常得不出正解。
但是,使用贪心算法时,这些空间可以帮助算法更容易实现且更快执行。
如果有正确贪心性质存在,那么一定要采用。
因为它容易编写,容易调试,速度极快,并且节约空间。
几乎可以说,此时它是所有算法中最好的。
但是应该注意,贪心算法有两大难点:(1)如何贪心怎样用一个小规模的解构造更大规模的解呢?总体上,这与问题本身有关。
但是大部分都是有规律的。
正因为贪心有如此性质,它才能比其他算法快。
具有应当采用贪心算法的问题,当“贪心序列”中的每项互异且当问题没有重叠性时,看起来总能通过贪心算法取得(近似)最优解的。
或者,总有一种直觉在引导我们对一些问题采用贪心算法。
其中“找零钱”这个问题就是一个例子。
题中给出的硬币面值事实上具有特殊性,如果面值发生变化,可能贪心算法就不能返回最优解了。
但是,值得指出的是,当一个问题具有多个最优解时,贪心算法并不能求出所有最优解。
另外,我们经过实践发现,单纯的贪心算法是顺序处理问题的;而且每个结果是可以在处理完一个数据后即时输出的。
(2)贪心的正确性要证明贪心性质的正确性,才是贪心算法的真正挑战,因为并不是每次局部最优解都会与整体最优解之间有联系,往往靠贪心算法生成的解不是最优解。
这样,贪心性质的证明就成了贪心算法正确的关键。
对某些问题贪心性质也许是错的,即使它在大部分数据中都是可行的,但还必须考虑到所有可能出现的特殊情况,并证明该贪心性质在这些特殊情况中仍然正确。
而这样容易陷入证明不正确贪心性质的泥塘中无法自拔,因为贪心算法的适用范围并不大,而且有一部分极难证明,若是没有把握,最好不要冒险,还有其他算法会比它要保险。
贪心算法存在的问题(1)不能保证求得的最后解是最佳的。
由于贪心策略总是采用从局部看来是最优的选择,因此并不从整体上加以考虑;(2)贪心算法只能用来求某些最大或最小解的问题;(3)贪心算法只能确定某些问题的可行性范围贪心算法的应用1哈夫曼编码2 0-1背包问题3磁盘文件的存储4生产调度问题5信息查询贪心算法经典应用举例删数问题问题提出:给定n位正整数a,去掉其中任意k<=n个数字后,剩下的数字按原次序排列组成一个新的正整数。
对于给定的n位正整数a和正整数k,设计一个算法找出剩下数字组成的新数最小的删数方案。
分析:n位数a可表示为x1x2…xixjxk…xn,要删去k位数,使得剩下的数字组成的整数最小。
设本问题为T,其最优解A=(y1,y2…yk)表示依次删去的k 个数,在删去k个数后剩下的数字按原次序排成的新数。
即最优值记为TA。
本问题采用贪心算法求解,采用最近下降点优先的贪心策略:即x1<x2<…<xi<xj;如果xk<xj,则删去xj,即得到一个新的数且这个数为n一1位中为最小的数Nl,可表示为x1x2…xixkxm…xn。
对N1而言,即删去了1位数后,原问题T变成了需对n-1位数删去k-1个数新问题T’。
新问题和原问题相同,只是问题规模由n减小为n-1,删去的数字个数由k减少为k-1。
基于此种删除策略,对新问题T’,选择最近下降点的数进行删除,如此进行下去,直至删去k个数为止。
证明:先来证明该问题具有贪心选择性质,即对问题T删除最近下降点的数xj后得到的N1是n一1位数是中最小的数。
根据数的进制特点,对a按权展开得:a=x1*10n-1+x2*10n-2+...+xi*10n-i+xj*10n-j+xk*10n-k+ (x)则有:Nl=x1*10n-2+x2*10n-3+...+xi*10n-i-1+xk*10n-k+ (x)假设删去的不是xj而是其它位,则有N2=x1*10n-2+x2*10n-3+...+xi*10n-i-1+xj*10n-k+ (x)因为有x1<x2<…<xi<xj且xj>xk,则有Nl<N2。
因此删数问题满足贪心选择性质。
删数问题的C++代码:#include<iostream>#include <string>using namespace std;int main(){string n;int s,i,x,l,m;printf("请输入一个正整数和将要删去的个数!\n");while(cin>>n>>s){i=-1,m=0,x=0;l=n.length();while(x<s&&m==0){i++;if(n[i]>n[i+1])//出现递减,删除递减的首数字{n=n.erase(i,1);x++;// x统计删除数字的个数i=-1;//从头开始查递减区间}if(i==l-x-2&&x<s)m=1;//已经无递减区间,m=1脱离循环}printf("最后结果为:\n");cout<<n.substr(0,l-s+x);//只打印剩下的左边l-(s-x)个数字}}问题的最优子结构性质在进行了贪心选择后,原问题T就变成了对N1如何删去k-1个数的问题T’,是原问题的子问题。
若A=(xj,A’)是原问题T的最优解,则A’是子问题T’的最优解,其最优值为TA’。
证明:假设A’不是子问题T’的最优解,其子问题的最优解为B’,其最优值记为TB’,则有TB’<TA’。
而根据TA的定义可知:TA= TA’+xj*1On-j,而TB’<TA’,因此有TB’+xj*1On-j<TA’+xj*1On-j=TA。
即存在一个由数a 删去1位数后得到的n-1位数比最优值TA更小。
这与TA为问题T的最优值相矛盾。
因此,A’是子问题T’的最优值。
因此,删数问题满足最优子结构性质。
从以上贪心选择及最优子结构性质的证明可知删数问题用贪心算法可以求得最优解。
活动安排问题问题:设有n个活动的集合E={1,2,...,n},其中每个活动都要求使用同一资源,如演讲会场等,而在同一时间内只有一个活动能使用这一资源。
每个活动i都有一个要求使用该资源的起始时间Si和一个结束时间Fi,且Si<Fi。
如果选择了活动i,则它在半开时间区间[Si,Fi)内占用资源。
若区间[Si,Fi)与区间[Sj,Fj)不相交,则称活动i与活动j是相容的。
也就是说Si>=Fj或Sj>=Fi时,活动i与活动j相容。
活动安排问题就是要在所给的活动集合中选择出最大的相容活动子集合。
证明:这个问题可以使用贪心算法进行求解。
其实这个问题的关键并不是如何用贪心算法求解,而是如何证明这个问题可以用贪心算法求解。
鉴于证明的复杂性,还是不在此讨论证明问题。
其实贪心算法问题的证明无非都是用数学归纳法证明,不错就是那个传说中万能证明法,数学归纳法。
还是直接讨论实现过程吧。
实现:首先将活动按照活动的结束时间非递减:F1<=F2<=...<=Fn排序。
如果所给的活动未排序,则先将活动按此序排列,时间复杂度一般为O(nlogn)可解决。
以下是解决问题的算法。
1 //贪心算法-活动安排的实现23 #include "stdafx.h"4 #include "stdio.h"56 template<class Type>7 void GreedySelector(int n,Type s[],Type f[],bool A[])8 {9 A[0]=1; //第一个直接选取10 int j=0;11 for(int i=1;i<n;i++)12 {13 if(s[i]>=f[j]){A[i]=true;j=i;} //如果第i+1的活动的开始时间大于或等于第i个活动的结束时间,则标记该活动14 else A[i]=false;15 }16 }1718 int _tmain(int argc, _TCHAR* argv[])19 {20 //初始化数据21 int n=3;22 int s[3]={1,2,4}; //开始时间23 int f[3]={3,3,5}; //结束时间24 bool A[3]={0,0,0}; //数组A用于标记是否选择活动,1表示选择,0表示不选择2526 GreedySelector<int>(n,s,f,A);27 for(int i=0;i<n;i++)28 {29 printf("%d\n",A[i]);30 }31 }该算法的贪心选择的意义是使剩余的可安排的时间段极大化,以便安排尽可能多的相容活动。
也就是说,每次选择完了之后,保证这次的选择之后留下的时间是最多的。
时间复杂度:GreedySelector算法效率极高。
当输入的数据是已经按照结束时间非递减排序好的时候,算法只需要O(n)的时间安排n个活动,使最多的活动能相容地使用公共资源。
总结贪心算法是很常见的算法,贪心策略是最接近人的日常思维的一种解题策略,虽然它不能保证求得的最后解一定是最佳的,但是它可以为某些问题确定一个可行性范围。
贪心算法所作的选择依赖于以往所作过的选择,但决不依赖于将来的选择,这使得算法在编码和执行过程中都有一定的速度优势。
对于一个问题的最优解只能用穷举法得到时,用贪心算法是寻找问题最优解的较好算法。
对一个问题可以同时用几种方法解决,贪心算法并不是对所有的问题都能得到整体最优解或是最理想的近似解时,就需判断贪心性质的正确性了。