贪心算法与动态规划的区别与联系

参考博客:https://blog.csdn.net/Mr_tyting/article/details/77850365

一、动态规划算法简介

  动态规划算法是通过拆分问题,定义问题状态和状态之间的关系,使得问题能够以递推(或者说分治)的方式去解决。

动态规划是一种分治思想(比如其状态转移方程就是一种分治),但与分治算法不同的是,分治算法是把原问题分解为若干个子问题,自顶向下求解子问题,合并子问题的解,从而得到原问题的解。动态规划也是把原始问题分解为若干个子问题,然后自底向上,先求解最小的子问题,把结果存在表格中,在求解大的子问题时,直接从表格中查询小的子问题的解,避免重复计算,从而提高算法效率。

  1.基本思想与策略

  动态规划算法的基本思想与分治法类似,也是将待求解的问题分解为若干个子问题(阶段),按顺序求解子阶段,前一子问题的解,为后一子问题的求解提供了有用的信息。在求解任一子问题时,列出各种可能的局部解,通过决策保留那些有可能达到最优的局部解,丢弃其他局部解。依次解决各子问题,最后一个子问题就是初始问题的解。由于动态规划解决的问题多数有重叠子问题这个特点,为减少重复计算,对每一个子问题只解一次,将其不同阶段的不同状态保存在一个二维数组中。

贪心算法与动态规划的区别与联系_第1张图片

  2.适用情况

  能采用动态规划求解的问题的一般要具有3个性质:

  (1)最优化原理:如果问题的最优解所包含的子问题的解也是最优的,就称该问题具有最优子结构,即满足最优化原理。

  (2)无后效性:即某阶段状态一旦确定,就不受这个状态以后决策的影响。也就是说,某状态以后的过程不会影响以前的状态,只与当前状态有关。

  (3)有重叠子问题:即子问题之间是不独立的,一个子问题在下一阶段决策中可能被多次使用到。(该性质并不是动态规划适用的必要条件,但是如果没有这条性质,动态规划算法同其他算法相比就不具备优势)

  3.算法实现

  动态规划的主要难点在于理论上的设计,也就是上面4个步骤的确定,一旦设计完成,实现部分就会非常简单。使用动态规划求解问题,最重要的就是确定动态规划三要素:

  (1)问题的阶段

  (2)每个阶段的状态

  (3)从前一个阶段转化到后一个阶段之间的递推关系。

  递推关系必须是从次小的问题开始到较大的问题之间的转化,从这个角度来说,动态规划往往可以用递归程序来实现,不过因为递推可以充分利用前面保存的子问题的解来减少重复计算,所以对于大规模问题来说,有递归不可比拟的优势,这也是动态规划算法的核心之处。

  确定了动态规划的这三要素,整个求解过程就可以用一个最优决策表来描述,最优决策表是一个二维表,其中行表示决策的阶段,列表示问题状态,表格需要填写的数据一般对应此问题的在某个阶段某个状态下的最优值(如最短路径,最长公共子序列,最大价值等),填表的过程就是根据递推关系,从1行1列开始,以行或者列优先的顺序,依次填写表格,最后根据整个表格的数据通过简单的取舍或者运算求得问题的最优解。

贪心算法与动态规划的区别与联系_第2张图片

通常背包问题就是采用的动态规划进行求解。

  二、贪心算法简介

 在讲贪心算法之前,我们可以先看一个问题(顺便说明了什么问题可以用动态规划但不能用贪心选择):
零钱问题:
大家在生活中都有找零钱的经历,假如每种面额的钱币的数量都足够多,那么如果让找出14块钱,那绝大多数人会用一种10元的两张2元(假设还有2元的纸币)的来凑足14元。或许你没有察觉到,其实在这个过程中我们已经用到了算法,即贪心算法。正如前文提到的,贪心算法就是根据人思维模式设计的算法,所以平常生活中有很多这样的例子。

先来说说找钱的具体过程。仔细琢磨一下我们的选择过程:因为14>10,所以选择一张10元的,还剩4元,正好用两个2元的凑足。这个例子比较小,可能还不够说明问题,我们这样来想,假入要找177元,该用几张1元的?快点儿,仔细想一下,到底用几张?怎么样,发现问题了吧。没人可以直接回答找177要用几张1元的,我们都是想先用一张100的,剩77再用一张50的……最后确定用几张1元的。这个过程大家已经经历了无数次了,所以已经形成条件发射了。但仔细想想,这就说明了贪心选择前的排序过程,也就是说我们的贪心选择必须从大面值的开始,而不能从小面额的开始,这就是我们所说的贪心策略。

再来看看我们这样做的依据。为什么我们要这么选择钱的种类呢?为什么我们可以这样选择呢?首先要明确我们的目标,为什么我们不用7张2元的来凑14元,显然我们的目的是使钱的总张数最少。那我们这样选择一定可以保证无论找多少零钱,总的张数都是最少的吗?答案肯定是不行,但这里面仍有一些细节需要了解。假如我们的纸币系统的面额不是1、2、5、10这样的数,假如是我们的是1、2、7、10,那我们要找14元,还可以用贪心选择策略来选择吗?显然就不能了,两个7元的显然是最优解,即用的张数最少的解。
    接下来正式介绍一下贪心算法:
    贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,他所做出的是在某种意义上的局部最优解。
因而贪心算法不是对所有问题都能得到整体最优解(也就是说这样的问题贪心算法就不适用,可以用动态规划),例如01背包问题就不能用贪心算法求解(因为01背包问题不具备贪心选择性质----通过局部最优,达不到全局最优),关键是贪心策略的选择,选择的贪心策略必须具备无后效性,即某个状态以前的过程不会影响以后的状态,只与当前状态有关。

贪心与动态规划的区别参考博客:https://blog.csdn.net/cyfcsd/article/details/50249953

能用贪心算法解决的问题理论上都可以利用动态规划解决,而一旦证明贪心选择性质,用贪心算法解决问题比动态规划具有更低的时间复杂度和空间复杂度,如果一个问题有一部分情况是不满足这个策略的,这时候就只能用动态规划了。

背包问题:上一次我们用0-1背包问题讲解了贪婪算法的应用,其中也提到了背包问题和0-1背包的区别。而这个区别也是贪心选择策略能否适用的区别,也是该用贪心算法还是改用动态规划的区别。这里我们不再仔细分析两个问题,只定性的看一下这两个问题的关键区别。

为什么物体可以分割时能用贪婪算法,而物体不能分割时就不行呢?这里数学证明就不细讲了,本人能力有限,而且各位估计也没心情看。这里只想让大家有一个什么情况需要“回退”的概念,这个需要“回退”的概念就是贪心选择性质失败的意思。

在背包问题中,如果物体可以分割,那我们装入单位质量最贵的东西“显然”是正确的,而如果物品不可分割,那就可能出现背包装了一个单价很贵的东西但没有装满,而后面一个虽然单价比较低但体积也比较大,这样就装不进去了,如果把前面那个东西倒出来把这个大的装进去可能就会使得总价值更大。总之这个问题在于背包可能装不满,而如果有一个物体单价低但占的空间更充分的话就有可能会得到更好的解。所以这个问题就需要往回试探的过程,这个就是要使用动态规划的标识
 

 

  1.基本要素

  (1)贪心选择(贪心策略)

  贪心选择是指所求问题的整体最优解可以通过一系列局部最优的选择,即贪心选择来达到。这是贪心算法可行的第一个基本要素,也是贪心算法与动态规划算法的主要区别。贪心选择是采用从顶向下、以迭代的方法做出相继选择,每做一次贪心选择就将所求问题简化为一个规模更小的子问题。对于一个具体问题,要确定它是否具有贪心选择的性质,我们必须证明每一步所作的贪心选择最终能得到问题的最优解。通常可以首先证明问题的一个整体最优解,是从贪心选择开始的,而且作了贪心选择后,原问题简化为一个规模更小的类似子问题。然后,用数学归纳法证明,通过每一步贪心选择,最终可得到问题的一个整体最优解。

  (2)最优子结构

  当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。运用贪心策略在每一次转化时都取得了最优解。问题的最优子结构性质是该问题可用贪心算法或动态规划算法求解的关键特征。贪心算法的每一次操作都对结果产生直接影响,而动态规划则不是。贪心算法对每个子问题的解决方案都做出选择,不能回退;动态规划则会根据以前的选择结果对当前进行选择,有回退功能。动态规划主要运用于二维或三维问题,而贪心一般是一维问题。

利用贪心思想的算法:Prim算法:

最小生成树MST:
最小生成树要求从一个带权无向连通图中选择 n-1n-1 条边并使这个图仍然连通(也即得到了一棵生成树),同时还要考虑使树的权最小。为了得到最小生成树,人们设计了很多算法,最著名的有 PrimPrim 算法和KruskalKruskal 算法,这两个算法都是贪心算法。


PrimPrim 算法是解决最小生成树的常用算法。它采取贪心策略,从指定的顶点开始寻找最小权值的邻接点。图 G=G= ,初始时 S=V0S=V0,把与 V0V0 相邻接,且边的权值最小的顶点加入到 SS。不断地把 SS 中的顶点与 V−SV−S 中顶点的最小权值边加入(不可能形成环),直到所有顶点都已加入到 SS 中。

实例:

PrimPrim 过程,假定从 V0 开始:

è¿éåå¾çæè¿°

  2.贪婪算法特性

  贪婪算法可解决的问题通常大部分都有如下的特性:

  随着算法的进行,将积累起其它两个集合:一个包含已经被考虑过并被选出的候选对象,另一个包含已经被考虑过但被丢弃的候选对象。

  有一个函数来检查一个候选对象的集合是否提供了问题的解答。该函数不考虑此时的解决方法是否最优。

  还有一个函数检查是否一个候选对象的集合是可行的,也即是否可能往该集合上添加更多的候选对象以获得一个解。和上一个函数一样,此时不考虑解决方法的最优性。

  选择函数可以指出哪一个剩余的候选对象最有希望构成问题的解。

  最后,目标函数给出解的值。

  为了解决问题,需要寻找一个构成解的候选对象集合,它可以优化目标函数,贪婪算法一步一步的进行。起初,算法选出的候选对象的集合为空。接下来的每一步中,根据选择函数,算法从剩余候选对象中选出最有希望构成解的对象。如果集合中加上该对象后不可行,那么该对象就被丢弃并不再考虑;否则就加到集合里。每一次都扩充集合,并检查该集合是否构成解。如果贪婪算法正确工作,那么找到的第一个解通常是最优的。

贪心算法与动态规划的区别与联系_第3张图片

你可能感兴趣的:(算法)