1 定义:
在求最优解问题的过程中,依据某种贪心标准,从问题的初始状态出发,直接去求每一步的最优解,通过若干次的贪心选择,最终得出整个问题的最优解,这种求解方法就是贪心算法。
从贪心算法的定义可以看出,贪心算法不是从整体上考虑问题,它所做出的选择只是在某种意义上的局部最优解,而由问题自身的特性决定了该题运用贪心算法可以得到最优解。
如果一个问题可以同时用几种方法解决,贪心算法应该是最好的选择之一。其中,解决问题最关键是寻找到合适的贪心标准,找到它,问题就解决了大部分!
2 贪心算法的理论基础
贪心算法是一种在每一步选择中都采取在当前状态下最好或最优的选择,希望得到结果是最好或最优的算法。
贪心算法是一种能够得到某种度量意义下的最优解的分级处理方法,通过一系列的选择得到一个问题的解,而它所做的每一次选择都是当前状态下某种意义的最好选择。即希望通过问题的局部最优解求出整个问题的最优解。
这种策略是一种很简洁的方法,对许多问题它能产生整体最优解,但不能保证总是有效,因为它不是对所有问题都能得到整体最优解。
利用贪心策略解题,需要解决两个问题:
(1)该题是否适合于用贪心策略求解;
(2)如何选择贪心标准,以得到问题的最优/较优解。
2.1 贪心选择性质
贪心选择性质是指所求问题的整体最优解可以通过一系列局部最优的选择,即贪心选择来达到。
2.2 最优子结构性质
当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。
运用贪心策略在每一次转化时都取得了最优解。问题的最优子结构性质是该问题可用贪心算法或动态规划算法求解的关键特征。
贪心算法的每一次操作都对结果产生直接影响。
贪心算法对每个子问题的解决方案都做出选择,不能回退。
//A是问题的输入集合即候选集合
Greedy(A)
{
S={ }; //初始解集合为空集
while (not solution(S)) //集合S没有构成问题的一个解
{
x = select(A); //在候选集合A中做贪心选择
if feasible(S, x) //判断集合S中加入x后的解是否可行
S = S+{x};
A = A-{x};
}
return S;
}
(1)候选集合A:问题的最终解均取自于候选集合A。
(2)解集合S:解集合S不断扩展,直到构成满足问题的完整解。
(3)解决函数solution:检查解集合S是否构成问题的完整解。
(4)选择函数select:贪心策略,这是贪心算法的关键。
(5)可行函数feasible:解集合扩展后是否满足约束条件。
3贪心法存在的问题:
1. 不能保证求得的最后解是最佳的;
2. 不能用来求最大或最小解问题;
3. 只能求满足某些约束条件的可行解的范围
上述的这些问题可以在动态规划 , 回溯算法 ,分支限界算法 里面得到相应的解决,但是我们也不会因此放弃对贪心算法的使用。这正是敏捷开发所提倡的,永远没有最好的,只有最适合的.我们选用贪心算法的原因就是因为他能够满足当前的需要并且比其他算法更加简单。
计算背包问题的贪心算法
//形参n是物品的数量,c是背包的容量M,数组a是按物计算背包问题的贪心算法
品的性价比降序排序
double knapsack(int n, bag a[], double c)
{
double cleft = c; //背包的剩余容量
int i = 0;
double b = 0; //获得的价值
//当背包还能完全装入物品i
while(i<n && a[i].w<cleft)
{
cleft -= a[i].w;
b += a[i].v;
i++;
}
//装满背包的剩余空间
if (i<n) b += 1.0*a[i].v*cleft/a[i].w;
return b;
}
问题:如何判断是否可以用贪心;
严格的证明 可使用
有反例就是 不能用
贪心算法的一个充分必要条件:矩阵胚拟理