在求最优解问题中,依据某种贪心标准,从问题的初始状态出发,直接去求每一步的最优解,通过若干次的贪心选择,最终得出整个问题的最优解,这种求解方法就是贪心算法。
当一个问题具有最优子结构性质和贪心选择性质时,贪心算法通过一系列的选择来得到一个问题的解。
5.2 贪心算法的理论基础
贪心算法是一种在每一步选择中都采取在当前状态下最好或最优的选择,即贪心选择,从而希望得到的结果是最好或最优的算法。
5.2.1 贪心选择性质
贪心选择性质是指所求问题可以通过一系列局部最优的选择,即**贪心选择**来得到。这是贪心算法可行的**第一个基本要素**,也是贪心算法和动态规划算法的**主要区别**。
-
在动态规划算法中,每步做出的选择往往依赖于相关子问题的解,因而只有在解出相关子问题后,才能做出选择。
-
在贪心算法中,仅在当前状态下做出最好选择,即局部最优选择,然后再去解出这个选择后产生的相应的子问题。贪心算法所做的贪心选择可以依赖于以往所做过的选择,但绝不依赖于将来所做的选择,也不依赖于子问题的解。
正是由于这种差别,动态规划算法通常以自底向上的方式解各子问题,而贪心算法则通过自顶向下的方式进行,以迭代的方式做出相继的贪心选择,每做一次贪心选择就将所求的问题简化为规模更小的子问题。
5.2.2 最优子结构性质
当一个问题的最优解包含其子问题的解时,称此问题具有最优子结构性质。
贪心算法的每一次操作都对结果产生直接影响,而动态规划则不是。贪心算法对每个子问题的解决方案做出选择,不能回退;动态规划则会根据以前的选择对当前方案进行选择,有回退功能。
动态规划主要运用于二维或三维问题,而贪心算法一般用于一维问题。
5.2.3 贪心算法的求解过程
使用贪心算法应考虑以下几个方面:
-
候选集合A
-
解集合S
-
解决函数solution
-
选择函数select
-
可行函数feasible
算法5.2 贪心算法的一般流程
// A是问题的输入集合,即候选集合
Greedy(A){
S = {}; // 初始解集为空
while (not solution(S)){ // 集合S没有构成一个解
x = select(A); // 在候选集A中做贪心选择
if (feasible(S, x)) // 判断集合S中加入x是否可行
S += {x}; // 在集合S中加入{x}
A -= {x}; // 候选集A中删去{x}
}
return S;
}