[经典贪心算法]贪心算法概述
贪心算法具有最优子问题结构,它的特点是“短视”,每次选择对当前局面最有利的决策,来一步步获得最优解。
我个人认为,贪心不是一个具体的方法,而是一类方法,贪心算法的关键不在于想到,而在于正确性的证明。要证明一个贪心算法是正确的,需要证明我们可以把一个最优解逐步转化为我们用贪心算法所得到的解,而解不会更差,从而证明贪心算法得到的解和最优解是一样好的(显然,最优解不可能更好)。而要证明一个贪心算法是错误的,只需要找到一个反例就可以了。
通常情况下,证明贪心算法是正确的或者找到贪心算法的一个反例都不那么容易。
而且即使对于同一个问题,从不同角度的贪心算法的正确性也不尽相同。例如Dijkstra算法是著名的求单源图最短路径的贪心算法。
如果我们也给出一个贪心算法,从源头开始每次选择最短的边继续走,直到走到,直到经过全部点或者无路可走。按照我们的算法,在上图中,A到C的最短路是A-B-C,它的长度是5,显然A-D-C才是真正的最短路。我们的贪心算法是错的。
一个问题即使不能使用某个贪心算法,也可以通过贪心算法给出一个“还说得过去”的解,这也是贪心算法在现实中存在的意义之一。
基本的算法中贪心著名的贪心算法包括: Dijskstr单源图最短路径算法、Prim和Kruskal最小生成树算法、Huffman编码简单压缩算法等。
如果给贪心算法一个抽象地描述,我认为可以这样讲: 假设一些对象的集合S, 每个对象x对应一个收益payoff(x),对于任意S的子集T,我们有一个函数可以判断它是否合法isValid(T) ——它返回布尔值。并且这个函数通常有个性质,空集是合法的;如果 T合法,它的任意子集都合法;如果它非法,它的任意超集都非法。我们的目标是从 S中选取若干个对象,形成一个集合V,使得isValid(V) == true并且payoff(V)尽可能大。其中payoff(V)定义为V中所有对象的收益之和。贪心算法是这么解决这个问题的,从空集合开始,每次选一个payoff最大并且合法的对象x加入到V里面, V = VU{x}。
可见具备上述性质的问题实际上还是比较特殊的,而上述性质通常成为贪心选择性。
可见贪心选择是比较“短视”的,选取最优的一个元素,即使有多个,任选一个。而动态规划算法是从所有能达到当前状态的状态和决策中选取,所以从某种角度上讲,动态规划是枚举——只是聪明点的枚举罢了,它枚举的是所有状态以及该状态下的决策。而贪心只是单一的选择,盲目选择当前最优的决策。
所以一般对于一个问题来说,我们只讲这样一个贪心算法是错误的,而不说这个问题不能采用贪心算法——因为可能从别的角度设计出的贪心算法是正确。
基本的算法中贪心著名的贪心算法包括: Dijskstr单源图最短路径算法、Prim和Kruskal最小生成树算法、Huffman编码简单压缩算法等。
如果给贪心算法一个抽象地描述,我认为可以这样讲: 假设一些对象的集合S, 每个对象x对应一个收益payoff(x),对于任意S的子集T,我们有一个函数可以判断它是否合法isValid(T) ——它返回布尔值。并且这个函数通常有个性质,空集是合法的;如果 T合法,它的任意子集都合法;如果它非法,它的任意超集都非法。我们的目标是从 S中选取若干个对象,形成一个集合V,使得isValid(V) == true并且payoff(V)尽可能大。其中payoff(V)定义为V中所有对象的收益之和。贪心算法是这么解决这个问题的,从空集合开始,每次选一个payoff最大并且合法的对象x加入到V里面, V = VU{x}。
可见具备上述性质的问题实际上还是比较特殊的,而上述性质通常成为贪心选择性。
可见贪心选择是比较“短视”的,选取最优的一个元素,即使有多个,任选一个。而动态规划算法是从所有能达到当前状态的状态和决策中选取,所以从某种角度上讲,动态规划是枚举——只是聪明点的枚举罢了,它枚举的是所有状态以及该状态下的决策。而贪心只是单一的选择,盲目选择当前最优的决策。
贪心和动态规划算法的比较可见下表:
本章主要讲述贪心算法,让我们一起来领略它的风采吧。
本章主要讲述贪心算法,让我们一起来领略它的风采吧。
所以一般对于一个问题来说,我们只讲这样一个贪心算法是错误的,而不说这个问题不能采用贪心算法——因为可能从别的角度设计出的贪心算法是正确。