贪心算法概论
五大算法思想—贪心算法

五⼤算法思想—贪⼼算法怎么理解 贪⼼法在解决这个问题的策略上⽬光短浅,仅仅依据当前已有的信息就做出选择,并且⼀旦做出了选择。
⽆论将来有什么结果,这个选择都不会改变。
⼀句话:不求最优,仅仅求可⾏解。
怎样推断 对于⼀个详细的问题,怎么知道是否可⽤贪⼼算法解此问题,以及是否能得到问题的最优解? 我们能够依据贪⼼法的2个重要的性质去证明:贪⼼选择性质和最优⼦结构性质。
1、贪⼼选择 什么叫贪⼼选择?从字义上就是贪⼼也就是⽬光短线。
贪图眼前利益。
在算法中就是仅仅依据当前已有的信息就做出选择,并且以后都不会改变这次选择。
(这是和动态规划法的主要差别) 所以对于⼀个详细问题。
要确定它是否具有贪⼼选择性质,必须证明每做⼀步贪⼼选择是否终于导致问题的总体最优解。
2、最优⼦结构 当⼀个问题的最优解包括其⼦问题的最优解时,称此问题具有最优⼦结构性质。
这个性质和动态规划法的⼀样,最优⼦结构性质是可⽤动态规划算法或贪⼼算法求解的关键特征。
区分动态规划 动态规划算法通常以⾃底向上的⽅式解各⼦问题,是递归过程。
贪⼼算法则通常以⾃顶向下的⽅式进⾏,以迭代的⽅式作出相继的贪⼼选择,每作⼀次贪⼼选择就将所求问题简化为规模更⼩的⼦问题。
以⼆叉树遍历为例: 贪⼼法是从上到下仅仅进⾏深度搜索。
也就是说它从根节点⼀⼝⽓⾛到⿊的,它的代价取决于⼦问题的数⽬,也就是树的⾼度,每次在当前问题的状态上作出的选择都是1。
不进⾏⼴度搜索。
所以终于它得出的解不⼀定是最优解。
⾮常有可能是近似最优解。
⽽动态规划法在最优⼦结构的前提下,从树的叶⼦节点開始向上进⾏搜索,⽽且在每⼀步都依据叶⼦节点的当前问题的状况作出选择,从⽽作出最优决策。
所以她的代价是⼦问题的个数和可选择的数⽬。
它求出的解⼀定是最优解。
⼀般求解过程 使⽤贪⼼法求解能够依据下⾯⼏个⽅⾯进⾏(终于也相应着每步代码的实现),以找零钱为例: 1、候选集合(C) 通过⼀个候选集合C作为问题的可能解。
贪心算法问题解决策略概述

贪心算法问题解决策略概述贪心算法(Greedy Algorithm)是一种简单而有效的问题解决策略,通过每一步的局部最优选择,最终达到全局最优解。
其基本思想是从问题的某个初始解出发,通过贪心的选择,逐步得到一个更优解,以求解整个问题。
本文将对贪心算法的基本原理以及应用领域进行概述。
一、贪心算法的基本原理贪心算法的核心思想是每一步都做出当前的最优选择,将问题分解为一系列子问题,并进行局部最优解的选择,最终得到全局最优解。
二、适用条件贪心算法适用于具有贪心选择性质的问题,即通过局部最优解可以得到全局最优解。
贪心选择性质是指每一步的选择只依赖于当前状态,不受其他步骤的影响。
三、典型应用1. 最小生成树问题:在一个连通图中,找出一个包含所有顶点的边的子集,使得这个子集形成一个树,且所有边的权值之和最小。
2. 哈夫曼编码问题:在编码问题中,通过给频率较高的字符分配较短的编码,从而使得编码的总长度最短。
3. 区间调度问题:给定一组区间,选择尽量多的互不重叠的区间。
4. 零钱支付问题:给定一定面额的硬币和一个要支付的金额,找出支付方式使得所需硬币的数量最少。
5. 背包问题的一些变体:如01背包问题、完全背包问题等。
四、算法步骤贪心算法的思路是通过局部最优解来构建全局最优解。
其一般步骤如下:1. 建立数学模型来描述问题。
2. 将问题分解为若干个子问题。
3. 对每个子问题求解,得到局部最优解。
4. 对所有子问题的局部最优解进行整合,得到原问题的最优解。
五、优缺点贪心算法的优点在于简单、高效,能够快速地找到一个近似最优解。
但是它也有一些缺点,即不能保证能够得到全局最优解,因为贪心策略是基于局部最优的选择,并不能考虑全局情况。
六、总结贪心算法作为一种简单而有效的问题解决策略,在许多实际问题中发挥着重要作用。
通过每一步的局部最优选择,贪心算法能够快速地找到一个近似最优解。
但需要注意的是,贪心算法并不能保证一定能够得到全局最优解,因此在具体应用中需要谨慎分析问题的特点,判断是否适合采用贪心算法。
贪心算法的概念和适用条件 -回复

贪心算法的概念和适用条件-回复什么是贪心算法?贪心算法(Greedy Algorithm)是一种以局部最优解为导向的算法思想,通过每一步选择当前状态下的最佳操作来达到整体最优解的目标。
贪心算法的核心思想是每次都做出当前看来最优的选择,以期望能够达到整体的最优解。
贪心算法通常用于一些问题中,即每一步的选择只依赖于当前状态,而不考虑将来可能出现的情况。
贪心算法的适用条件:1. 贪心选择性质:贪心算法每一步都选择一个当前的最优解,此处的“最优”指的是局部最优。
这种最优选择可以确保问题能够被拆解,并且进行下一步求解。
2. 最优子结构性质:当问题的整体最优解能够通过局部最优解得到时,可以采用贪心算法求解。
这种情况下,问题的最优解可以由子问题的最优解推导出来。
3. 无后效性:贪心算法选择某一步操作时,只考虑当前状态,不会改变以前的操作,并且不关心未来的操作。
这种无后效性使得贪心算法在实际应用中操作简单、效率高。
贪心算法的基本步骤:1. 确定问题的局部最优解:贪心算法的核心是每一步都选择在当前情况下的最优解。
因此,需要确定问题如何拆解以及如何进行局部最优选择。
2. 定义问题的子问题:根据问题的最优子结构性质,将问题拆解为较小规模的子问题。
子问题应该是原问题的一个更小、更简单的实例。
3. 定义贪心选择策略:根据问题的特性,确定当前步骤下的最优选择策略。
这个选择应该是局部最优的,可以在不考虑子问题和整体未来状态的情况下得出。
4. 重复执行步骤2和3,直至求解出全局最优解。
贪心算法的优缺点:贪心算法具有简单易懂、快速高效的特点,适用于许多实际问题。
它可以避免穷举所有可能性,节省了计算时间。
此外,贪心算法常常能够找到近似最优解,尽管不一定能够保证全局最优解。
在实际问题中,近似最优解也往往可以满足实际需求。
然而,贪心算法并非适用于所有问题。
由于贪心算法只考虑当前状态的最优选择,而不考虑未来的影响,因此可能会导致局部最优解与全局最优解不一致。
五大常用算法之三:贪心算法

五⼤常⽤算法之三:贪⼼算法贪⼼算法的定义:贪⼼算法是指在对问题求解时,总是做出在当前看来是最好的选择。
也就是说,不从整体最优上加以考虑,只做出在某种意义上的局部最优解。
贪⼼算法不是对所有问题都能得到整体最优解,关键是贪⼼策略的选择,选择的贪⼼策略必须具备⽆后效性,即某个状态以前的过程不会影响以后的状态,只与当前状态有关。
解题的⼀般步骤是:1.建⽴数学模型来描述问题;2.把求解的问题分成若⼲个⼦问题;3.对每⼀⼦问题求解,得到⼦问题的局部最优解;4.把⼦问题的局部最优解合成原来问题的⼀个解。
如果⼤家⽐较了解动态规划,就会发现它们之间的相似之处。
最优解问题⼤部分都可以拆分成⼀个个的⼦问题,把解空间的遍历视作对⼦问题树的遍历,则以某种形式对树整个的遍历⼀遍就可以求出最优解,⼤部分情况下这是不可⾏的。
贪⼼算法和动态规划本质上是对⼦问题树的⼀种修剪,两种算法要求问题都具有的⼀个性质就是⼦问题最优性(组成最优解的每⼀个⼦问题的解,对于这个⼦问题本⾝肯定也是最优的)。
动态规划⽅法代表了这⼀类问题的⼀般解法,我们⾃底向上构造⼦问题的解,对每⼀个⼦树的根,求出下⾯每⼀个叶⼦的值,并且以其中的最优值作为⾃⾝的值,其它的值舍弃。
⽽贪⼼算法是动态规划⽅法的⼀个特例,可以证明每⼀个⼦树的根的值不取决于下⾯叶⼦的值,⽽只取决于当前问题的状况。
换句话说,不需要知道⼀个节点所有⼦树的情况,就可以求出这个节点的值。
由于贪⼼算法的这个特性,它对解空间树的遍历不需要⾃底向上,⽽只需要⾃根开始,选择最优的路,⼀直⾛到底就可以了。
话不多说,我们来看⼏个具体的例⼦慢慢理解它:1.活动选择问题这是《算法导论》上的例⼦,也是⼀个⾮常经典的问题。
有n个需要在同⼀天使⽤同⼀个教室的活动a1,a2,…,an,教室同⼀时刻只能由⼀个活动使⽤。
每个活动ai都有⼀个开始时间si和结束时间fi 。
⼀旦被选择后,活动ai就占据半开时间区间[si,fi)。
如果[si,fi]和[sj,fj]互不重叠,ai和aj两个活动就可以被安排在这⼀天。
算法设计与分析 第五章 贪心算法

第五章 贪心算法§1.贪心算法基本思想找零钱 假如售货员需要找给小孩67美分的零钱。
现在,售货员手中只有25美分、10美分、5美分和1美分的硬币。
在小孩的催促下,售货员想尽快将钱找给小孩。
她的做法是:先找不大于67美分的最大硬币25美分硬币,再找不大于67-25=42美分的最大硬币25美分硬币,再找不大于42-25=17美分的最大硬币10美分硬币,再找不大于17-10=7美分的最大硬币5美分硬币,最后售货员再找出两个1美分的硬币。
至此,售货员共找给小孩6枚硬币。
售货员的原则是拿尽可能少的硬币个数找给小孩。
从另一个角度看,如果售货员将捡出的硬币逐一放在手中,最后一起交给小孩,那么售货员想使自己手中的钱数增加的尽量快些,所以每一次都尽可能地捡面额大的硬币。
装载问题 有一艘大船用来装载货物。
假设有n 个货箱,它们的体积相同,重量分别是n w w w ,,21 ,货船的最大载重量是c 。
目标是在船上装最多货箱该怎样装?如果用1=i x 表示装第i 个货箱,而0=i x 表示不装第i 个货箱,则上述问题是解优化问题:求n x x x ,,,21 ,∑=ni i x 1max (5.1.1)c x i ni i ≤∑=1w (5.1.2)贪心方法,顾名思义,是在决策中总是作出在当前看来是最好的选择。
例如找零钱问题中,售货员每捡一个硬币都想着使自己手中的钱尽快达到需要找钱的总数。
在装载问题中,每装一个货箱都想着在不超重的前提下让船装更多的箱子。
但是贪心方法并未考虑整体最优解,它所作出的选择只是在某种意义上的局部最优选择。
当然,在采用贪心算法时未必不希望结果是整体最优的。
事实上,有相当一部分问题,采用贪心算法能够达到整体最优,如前面的找零钱问题以及后面将要讲到的单点源最短路径问题、最小生成树问题、工件排序问题等。
为了更好理解贪心算法,我们将装载问题稍加推广,考虑可分割的背包问题。
背包问题 已知容量为M 的背包和n 件物品。
贪心算法概述

贪⼼算法概述贪⼼,顾名思义,就是在把⼀个⼤问题分割成⽆数个相类似的⼦结构之后,对于每⼀个⼦结构,只在乎当前,贪⼼考虑最优选择,⽆需考虑整体最优。
⽤专业⼀点的术语来说,就是⽆后效性。
具体来说,⾯对⼀个⼤问题,截取当前的⼀⼩部分,在这个⼩部分中选择最优最好的结果。
然后以⼀种迭代,也就是递推的⽅式选取相似的下⼀⼩部分。
每次⾯对⼀⼩部分问题,保存最好的结果,丢进集合中。
最后在考虑整体的时候,贪⼼的结果就是集合⾥这些每个⼩部分问题保存下来的结果的综合。
贪⼼算法是指在对问题求解时,总是做出在当前看来是最好的选择。
也就是说,不从整体最优上加以考虑,只做出在某种意义上的局部最优解。
贪⼼算法不是对所有问题都能得到整体最优解,关键是贪⼼策略的选择,选择的贪⼼策略必须具备⽆后效性,即某个状态以前的过程不会影响以后的状态,只与当前状态有关。
贪⼼算法的基本要素:1.贪⼼选择性质。
所谓贪⼼选择性质是指所求问题的整体最优解可以通过⼀系列局部最优的选择,即贪⼼选择来达到。
这是贪⼼算法可⾏的第⼀个基本要素,也是贪⼼算法与动态规划算法的主要区别。
动态规划算法通常以⾃底向上的⽅式解各⼦问题,⽽贪⼼算法则通常以⾃顶向下的⽅式进⾏,以迭代的⽅式作出相继的贪⼼选择,每作⼀次贪⼼选择就将所求问题简化为规模更⼩的⼦问题。
对于⼀个具体问题,要确定它是否具有贪⼼选择性质,必须证明每⼀步所作的贪⼼选择最终导致问题的整体最优解。
2. 当⼀个问题的最优解包含其⼦问题的最优解时,称此问题具有最优⼦结构性质。
问题的最优⼦结构性质是该问题可⽤动态规划算法或贪⼼算法求解的关键特征。
贪⼼算法的基本思路:从问题的某⼀个初始解出发逐步逼近给定的⽬标,以尽可能快的地求得更好的解。
当达到算法中的某⼀步不能再继续前进时,算法停⽌。
该算法存在问题:1. 不能保证求得的最后解是最佳的;2. 不能⽤来求最⼤或最⼩解问题;3. 只能求满⾜某些约束条件的可⾏解的范围。
实现该算法的过程:从问题的某⼀初始解出发;while 能朝给定总⽬标前进⼀步 do 求出可⾏解的⼀个解元素;由所有解元素组合成问题的⼀个可⾏解;⽤背包问题来介绍贪⼼算法:背包问题:有⼀个背包,背包容量是M=150。
0021算法笔记——【贪心算法】贪心算法与精彩活动安排问题
0021算法笔记——【贪心算法】贪心算法与活动安排问题1、贪心算法(1)原理:在对问题求解时,总是做出在当前看来是最好的选择。
也就是说,不从整体最优上加以考虑,他所做出的仅是在某种意义上的局部最优解。
贪心算法不是对所有问题都能得到整体最优解,但对范围相当广泛的许多问题他能产生整体最优解或者是整体最优解的近似解。
(2)特性:贪心算法采用自顶向下,以迭代的方法做出相继的贪心选择,每做一次贪心选择就将所求问题简化为一个规模更小的子问题,通过每一步贪心选择,可得到问题的一个最优解,虽然每一步上都要保证能获得局部最优解,但由此产生的全局解有时不一定是最优的,所以贪婪法不要回溯。
能够用贪心算法求解的问题一般具有两个重要特性:贪心选择性质和最优子结构性质。
1)贪心选择性质所谓贪心选择性质是指所求问题的整体最优解可以通过一系列局部最优的选择,即贪心选择来达到。
这是贪心算法可行的第一个基本要素。
贪心算法则通常以自顶向下的方式进行,以迭代的方式作出相继的贪心选择,每作一次贪心选择就将所求问题简化为规模更小的子问题。
对于一个具体问题,要确定它是否具有贪心选择性质,必须证明每一步所作的贪心选择最终导致问题的整体最优解。
证明的大致过程为:首先考察问题的一个整体最优解,并证明可修改这个最优解,使其以贪心选择开始。
做了贪心选择后,原问题简化为规模更小的类似子问题。
然后用数学归纳法证明通过每一步做贪心选择,最终可得到问题的整体最优解。
其中,证明贪心选择后的问题简化为规模更小的类似子问题的关键在于利用该问题的最优子结构性质。
2)最优子结构性质当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。
(3)贪心算法与动态规划算法的差异:动态规划和贪心算法都是一种递推算法,均有最优子结构性质,通过局部最优解来推导全局最优解。
两者之间的区别在于:贪心算法中作出的每步贪心决策都无法改变,因为贪心策略是由上一步的最优解推导下一步的最优解,而上一部之前的最优解则不作保留,贪心算法每一步的最优解一定包含上一步的最优解。
算法导论-贪心算法
贪心算法可以看作是动态规划的一种特例,其中只保 留了与当前状态和当前选择相关的子问题的解,而不
是保留所有子问题的解。
贪心算法通常在每一步选择中只考虑当前状态下的局 部最优解,而不是考虑所有可能的选择和未来的状态。
贪心算法的经述 • 贪心算法的基本思想 • 贪心算法的经典问题 • 贪心算法的实现与优化 • 贪心算法的性能分析 • 总结与展望
贪心算法概述
01
定义与特点
定义
贪心算法是一种在每一步选择中都采取当前情况下最好或最优(即最有利)的 选择,从而希望导致结果是最好或最优的算法。
无法保证全局最优解
贪心算法可能只得到局部最优解,而非全局最 优解。
缺乏理论支持
贪心算法的理论基础尚不完备,难以对所有情况做出准确预测。
贪心算法与其他算法的比较
与动态规划算法比较
动态规划算法通过将问题分解为子问题来求解,适合处理具有重叠子问题和最优子结构的问题;而贪 心算法则是每一步都选择当前最优的选择,希望这样的局部最优选择能够导致全局最优解。
法的时间复杂度。
需要注意的是,贪心算法并不总是能够提供最优解,因此在实际应用中, 需要权衡时间复杂度和解的质量。
空间复杂度分析
贪心算法的空间复杂度主要取决于算法 的存储需求。在某些情况下,贪心算法 可能需要大量的存储空间来存储中间结
果或数据结构。
在分析贪心算法的空间复杂度时,需要 考虑算法所需的存储空间和存储结构的 复杂性。通过将每个存储需求的空间复 杂度相加,可以得出整个算法的空间复
与分治算法比较
分治算法是将问题分解为若干个子问题,然后递归地求解这些子问题,再将子问题的解合并起来得到 原问题的解;而贪心算法是在每一步都做出在当前状态下最好或最优(即最有利)的选择,希望这样 的局部最优选择能够导致全局最优解。
算法设计与分析讲义贪心法
贪心算法在每一步选择时都采取在当前状态下最好或最优( 即最有利)的选择,而不考虑可能出现的后果。
贪心算法与动态规划
贪心算法是一种局部最优策略,即每一步选择在当前状态下 是最优的,但不一定能够得到全局最优解。
动态规划是一种全局最优策略,通过将问题分解为子问题, 逐步求解得到全局最优解。
贪心算法的历史与发展
选择适当的贪心策略
选择适当的贪心策略可以最大程度 地减少计算时间。
空间优化
通过优化算法的空间复杂度,减少 算法的空间占用,以提高算法的效 率。
并行计算
利用并行计算技术,将算法并行化 ,以提高算法的运行速度。
数据结构优化
使用合适的数据结构可以加快算法 的执行速度,如哈希表、堆等。
06
贪心算法的应用扩展
贪心算法的优化策略
总结词
记忆化搜索、动态规划优化、全局状态决策
详细描述
贪心算法虽然能够得到问题的近似解,但在某些情况 下可能无法得到最优解。为了提高贪心算法的性能, 可以采用一些优化策略,例如记忆化搜索,将已经计 算过的状态存储下来,避免重复计算;动态规划优化 ,将问题分解为子问题,通过解决子问题来解决原问 题;全局状态决策,将问题的所有状态作为一个整体 来考虑,利用问题的整体性质来得到更好的解。
在资源分配问题中,贪心算法可以用于求解具有单位资源的最大权值
组合问题、分数背包问题等。
05
贪心算法的实现技巧
贪心算法的编程实现
确定贪心策略
根据问题特性确定贪心策略,通常选择最优解或近似最优解。
编码实现
将贪心策略转化为代码实现,通常使用循环和条件语句实现算 法。
测试样例
设计测试样例,覆盖各种情况,提高算法的健壮性和正确性。
贪心算法讲义
float p[1..n], w[1..n], x[1..n], M, rc;
integer i, n; x:= 0; // 将解向量初始化为零
//w[1..n],它们元素的排列 //顺序满p[i]/w[i]≥p[i+1]/w[i+1] //M是背包容量,x[1..n]是解向量
rc:= M; // 背包的剩余容量初始化为M
作业调度问题
单机作业调度问题的贪心算法
将上述算法找到的作业集按作业期限升序排列,就产生一 个作业调度。
算法的复杂度分析
J {i}的相容性判断最坏|J|次,|J|最大i-1。所以 T(n)=1+2+…+n-1=O(n2)
例子: 设n=7,
(p1, p2,… ,pn)=(35,30,25,20,15,10,5), (d1, d2,… ,dn)=(4,2,4,3,4,8,3) 算法GreedyJob的执行过程 : 作业 1 ;2, 1; 2, 1, 3; 2, 4, 1, 3; 2, 4, 1, 3 , 6; 期限 4; 2, 4; 2,4, 4; 2, 3, 4, 4; 2, 3, 4, 4, 8;
带期限的单机作业安排问题
已知n项作业 E={1, 2, … ,n}要求使用同台机器完成, 而且每项作业需要的时间都是1。第k项作业要求在时 刻2, d…k之, n前。完成, 而且完成这项作业将获得效益pk,k=1,
作业集E的子集称为相容的如果其中的作业可以被安 排由一台机器完成。
带限期单机作业安排问题就是要在所给的作业集合中 选出总效益值最大的相容子集。
许多NP难组合优化问题,目前仍未找到有效的算法, 贪心策略常用于设计这些问题的近似算法。
贪心算法的基本思想
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
贪心算法概论贪心算法一般来说是解决“最优问题”,具有编程简单、运行效率高、空间复杂度低等特点。
是信息学竞赛中的一个有为武器,受到广大同学们的青睐。
本讲就贪心算法的特点作些概念上的总结。
一、贪心算法与简单枚举和动态规划的运行方式比较贪心算法一般是求“最优解”这类问题的。
最优解问题可描述为:有n个输入,它的解是由这n 个输入的某个子集组成,并且这个子集必须满足事先给定的条件。
这个条件称为约束条件。
而把满足约束条件的子集称为该问题的可行解。
这些可行解可能有多个。
为了衡量可行解的优劣,事先给了一个关于可行解的函数,称为目标函数。
目标函数最大(或最小)的可行解,称为最优解。
a)求“最优解”最原始的方法为搜索枚举方案法(一般为回溯法)。
除了极简单的问题,一般用深度优先搜索或宽度优先搜索。
通常优化方法为利用约束条件进行可行性判断剪枝;或利用目标函数下界(或上界),根据当前最优解进行分枝定界。
b)其次现今竞赛中用的比较普遍的动态规划(需要满足阶段无后效性原则)。
动态规划主要是利用最最优子问题的确定性,从后向前(即从小规模向大规模)得到当前最优策略,从而避免了重复的搜索。
举例说明:求多段图的最短路径。
在图(1)中,我们省略了各线段的长度。
如果用回溯法,搜索树大致如下:显然,上面的搜索有大量重复性工作。
比如节点8、9、10到11的最短路分别被调用了9次,从节点5、6、7到节点11也分别搜索了3次。
如果先算出节点8、9、10到11的最短路,由于它与前面的点无关,因此最优值确定下来,再用它们求定节点5、6、7 到节点11 的最短路径。
同理,再用节点5、6、7 的最优值,来求节点2、3、4 优值。
最后从节点2、3、4 推出1 到11的最优值。
显然复杂度大为降低。
当然,如果本题把简单搜索改为搜索+记忆化的方法,则就是得能动态规划的原理,本质上就是动态规划,只是实现的方法不同与传统的表格操作法。
搜索+记忆化算法有其特有的特点,以后再讨论。
c)贪心算法则不同,它不是建立在枚举方案的基础上的。
它从前向后,根据当前情况,“贪心地”决定出下一步,从而一步一步直接走下去,最终得到解。
假如上面的例子中,我们定下这样的贪心策略:节点号k%3= =1。
则有图3:显然,它只访问了节点1、4、7、10、11,工作量最少,效率最高。
当然,对本题来说,它不能得到最优解―――最短路径。
从图3中可以看出,贪心算法是一种比动态规划更高效的算法。
只是要保证得到最优解是贪心算法的关键。
贪心算法的一般框架为:Procedure Greedy(A,n)//A问题有n个输入BeginInit(ans) //初始化for i:=1 to n donow=select(A) //按设计的标准选取一个“节点”delete(A,now) //把A中的now删掉if can(ans,now) then unio(ans,now) //如果可行性通过,放入解中。
end forend;二、贪心算法的关键――――正确性证明动态规划要满足最优子结构原则,贪心算法呢?就目前的资料看,有个证明模型是“矩阵胚理论”,但要证明一个问题是矩阵胚,十分困难。
并且也不常见的可用贪心算法问题都能用矩阵胚来证明。
因此,可以认为贪心算法的正确性证明是个难点。
因此有些选手在没能证明时,也大胆设想结论应该是正确的。
有时这难免会放错。
例如95年NOI中的“石子合并”一题:例一、石子合并试题:在一个圆形操场的四周摆放N 堆石子(N≤100),现要将石子有次序地合并成一堆。
规定每次只能选相邻的两堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。
编一程序,由文件读入堆数N 及每堆石子数(≤20)选择一种合并石子的方案,使得做N-1 次合并,得分的总和最小;选择一种合并石子的方案,使得做N-1 次合并,得分的总和最大。
例如,图4所示的4堆石,每堆石子数(从最上面的一堆数起,顺时针数)依次为4594。
则3次合并得分总和最小的方案为图5,得分总和最大的方案为图6。
输入数据文件名由键盘输入,该文件内容为:第1行为石子堆数N;第2行为每堆石子数,每两个数之间用一个空格符分隔。
输出数据输出文件名为output.txt ;从第1 至第N 行为得分最小的合并方案。
第N+1 行是空行。
从第N+2 行 到第2N+1行是得分最大合并方案。
每种合并方案用N 行表示,其中第i 行(1≤i ≤N)表示第i 次合并前各堆石 子数(依顺时针次序输出,哪一堆先输出均可)。
要求将待合并的两堆石子数以相 应的负数表示,以便标识。
输入输出示例:当时这题是第一次出现,几乎有半数以上人都选择了贪心算法―――每次选 相邻和最小的两堆石子合并。
其实由于只能是相邻的两堆石子合并,并不能套用经典的哈夫曼树算法。
本 题给的样例数据实际上是一个“陷阱”,造成了用贪心法即可解决的假象。
当一个贪心算法不能确定其正确性时,在使用之前,应该努力去证明它的不 正确性。
而要证明不正确性,一种最简单的形式就是举一个反例。
本例的反例见 图7。
常见的证明方法:贪心算法的正确性证明虽然不容易,但一些常见的方法还是值得总结的。
a) 构造法贪心算法的最小值为: (2+3)=5 (4+5)=9 (4+5)=9 (9+6)=15 (15+9)=245+9+9+15+24=62另一种方法的最小值为: (2+4)=6 (3+4)=7 (5+6)=11 (7+6)=13 (11+13)=246+7+11+13+24=61根据描述的算法,用贪心的策略,依次构造出一个解,可证明一定是合法的解。
即用贪心法找可行解。
b) 反证法用贪心的策略,依次构造出一个解S1。
假设最优解S2不同与S1,可以证明是矛盾的。
从而S1就是最优解。
c) 调整法用贪心的策略,依次构造出一个解S1。
假设最优解S2不同与S1,找出不同之处,在不破坏最优性的前提下,逐步调整S2,最终使其变为S1。
从而S1也是最优解。
下面分别举例说明。
例2 士兵排队问题(1996 年中国队选拔赛)----构造法证明试题:有N 个士兵(1≤N≤26),编号依次为A,B,C,...。
队列训练时,指挥官要把一些士兵从高到矮依次排成一行,但现在指挥官不能直接获得每个人的身高信息,只能获得“P1比P2高”这样的比较结果(P1,P2∈A,B,C,...,Z,记为P1>P2),如“A>B”表示A 比B高。
请编一程序,根据所得到的比较结果求出一种符合条件的排队方案。
注:比较结果中没有涉及到的士兵不参加排队输入数据比较结果从文本文件中读入(文件名由键盘输入),每个比较结果在文本文件中占一行。
输出要求若输入数据无解,打印“No Answer!”信息,否则从高到矮依次输出每一个士兵的编号,中间无分隔符,并把结果写入文本文件中,文件名由键盘输入。
输入输出示例INPUT.TXT OUTPUT.TXTA>B AFBDB>DF>D显然用每次选“偏序中最高士兵”的贪心算法就可构造出一个可行解。
这个问题转就是“拓扑排序”问题。
可这样证明:如果有解,就一定无环,因此每步贪心都一定能进行下去。
经典的“找欧拉回路”问题也类似。
例3 排队问题----反证法证明试题:在一个超市,有N 个人排队到一个柜台付款,已知每个人需要处理的时间为Ti(0<i≤N),请你找一种排列次序,使每个人排队的时间总和最小。
输入数据文本文件中第一行为一个正整数N≤10000,第二行有N个不超过1000的正整数Ti。
输出要求只一个正整数:大家排队时间的最小总和。
输入输出示例INPUT.TXT OUTPUT.TXT4 675 10 8 7本题的贪心算法为:N个数据从小到大排序,就是这N个人的最佳排序方案。
求部分和的和即可得到答案。
反证法证明如下:假设有最优解序列S1,S2,S3….,Sn,如果S1 不是最小的Tmin,不妨设Sk=Tmin,将S1 与Sk对调。
显然对Sk之后的人没影响,对Sk之前的人等待时间都减少了(S1-Sk)>0。
从而新的序列比原最优解序列好,矛盾!固S1为最小时间。
同理可证S2是第二小的数,……。
证毕。
下面的调整法也有类似的思想,只是侧重点在“这样调整不会差”。
例4 切巧克力问题----调整法证明试题我们提供给你一块m*n 的巧克力(如图8 所示),你需要将这块巧克力切割成1*1 的单位巧克力。
具体来说,对于一块巧克力,你有两种切割的方法。
一种是沿着某条竖线将巧克力切成两块,另外一种自然是沿着某条横线将巧克力切成两块。
切割需要一定的花费,费用的计算与所切割的巧克力的大小没有关系,而只于沿着哪条线切割有关。
我们将沿着横线切割的费用定义为y1、y2、……、y m-1;同样的,将沿着竖线切割的费用定义为x1、x2、……、x n-1。
现在,需要你计算的就是,怎样切才能使总费用最少?在图8 中,如果我们先沿着横线将整块巧克力切成四块,然后对于每一小块,分别沿着竖线切割,那么总共的费用就是y1+ y 2+ y3+ ( x1+ x2+ x3+ x4) ⨯ 4。
【输入格式】输入文件chocolate.in 的第一行包含两个整数n,m(1 ≤ n,m ≤ 1000),表示巧克力的大小。
接下来的n-1行每行包含一个整数,分别表示沿竖线切割的费用(上图中x1,x2,……),接下来的m-1行也是每行包含一个整数,表示沿横线切割的费用(上图中的y1,y2,……)【输出格式】输出文件chocolate.out 应该包含一个整数,表示你得到的最小切割费用。
样例输入样例输出6 4 4221314412分析:不妨设,Xi已经从大到小排序;Yi也同样。
如果把问题简化,只有横的切Xi或只有竖的切Yi,则显然和排队问题是一样的,可以用贪心算法。
横、竖可以交错切呢?对任意一种方案,只看其中横切的Xi 序列,如果不是从大到小排列,找到第一个违反有序原则的,设为Xp,找出应该在这个位置的――即后面不小于Xp的Xq,把Xp与Xq对调,显然新方案会更好。
如此,有结论一:任意最优方案在不改变竖切次序前提下,可调整成横切是有序的最优解。
同理,有结论二:任意最优方案在不改变横切次序前提下,可调整成竖切是有序的最优解。
综合结论一和结论二,有结论三:任意一种最优方案,可调整成横切、竖切都有序的最优解。
如此,可简单得到动态规划算法,时空复杂度约为O(106)。
上面虽然用了贪心算法的反证法解决了问题,但没有将贪心算法思想进行到底。
我们可以进一步用调整法和反证法的思想,将最优解调整到不分横切、竖切,都是递减的。
简单证明如下:设有最优方案序列XY[1..n+m],序列中任意两相邻项XY[i]和XY[i+1],记为a和b,若a≤b,分三种情况考虑:(1) 若a、b同是横切,显然交换a、b不影响最优解;(2) 若a、b同是竖切,显然交换a、b也不影响最优解;(3) a和b不同,最交换a、b后,增加费用a,减少费用为b,共增加费用为(a-b) ≤0,不会破坏最优性。