基本思想
从问题的某一个初始解出发,通过一系列的贪心选择——当前状态下的局部最优选择,逐步逼近给定的目标,尽可能快地求得更好的解。
在贪心算法(greedy method)中也采用逐步构造最优解的方法。在每个阶段,都作出一个按某个评价函数最优的决策,该评价函数最优称为贪心准则(greedy criterion)。
贪心算法的正确性,就是要证明按贪心准则求得的解是全局最优解。
贪心算法不能对所有问题都得到全局最优解。但对许多问题它能产生全局最优解,如单源最短路经问题,最小生成树问题等。
求解步骤
从问题的某一初始解出发;
while依据贪心策略朝给定目标前进一步do
求出可行解的一个解元素;
由所有解元素组合成问题的一个可行解;
适合求解问题的特征
贪心选择性质:可通过局部最优(贪心)选择达到全局最优解;
- 通常以自顶向下的方式进行,每次选择后将问题转化为规模更小的子问题;
- 该性质是贪心法使用成功的保障,否则得到的是近优解;
最优子结构性质:问题的最优解包含它的子问题的最优解;
- 并不是所有具有最优子结构性质的问题都可以采用贪心策略;
- 往往可以利用最优子结构性质来证明贪心选择性质;
存在的问题
如果不满足贪心选择性质,贪心算法存在的问题有:
- 不能保证求得的最后解是最优的;
- 只能求满足某些约束条件范围的局部最优解。
注:贪心算法虽不能保证得到最优结果,但对于一些除了“穷举”方法外没有有效算法的问题,用贪心算法往往能很快地得出较好的结果,此方法还是很实用的。
与动态规划算法的比较
DynamicProgramming
- At each step, the choice is determined based on solutionsof subproblems.
- Sub-problems are solved first.
- Bottom-up approach
- Can be slower, more complex
GreedyAlgorithms
- At each step, we quickly make a choice that currentlylooks best. -A local optimal (greedy) choice.
- Greedy choice can be made first before solving furthersub-problems.
- Top-down approach
- Usually faster, simpler
示例一:小数背包问题
按价值率最大贪心,使单位重量价值增长最快
按价值率排序从大到小选择物品
GreedyKnapsack(n, M, v[], w[], x[] ){
//按价值率最大贪心
Sort(n, v, w); //使v1/w1≥v2/w2≥…≥vn/wn
for i=1 to n do x[i]=0;
c=M;
for i=1 to n do{
if(w[i]>c) break;
x[i]=1;
c-=w[i];
}
if(i<=n) x[i]=c/w[i]; //物品i是选择的最后一项
}
时间复杂度T(n)=O(nlogn)
贪心选择的最优性证明的基本思想
把贪心解与任一最优解相比较,如果这两个解不同,就去找开始不同的第一个xi,然后设法用贪心解的xi去代换最优解的xi,并证明最优解在分量代换之后其总价值保持不变,反复进行下去,直到新产生的最优解与贪心解完全一样,从而证明了贪心解是最优解。
示例二:活动安排
有n个活动集E={1,2,…,n}使用同一资源,而同一时间内同一资源只能由一个活动使用。每个活动的使用时间为[si, fi) i=1,…,n,si为开始时间,fi为结束时间,若[si, fi)与[sj, fj)不相交称活动i和活动j是相容的
选出最大的相容活动子集合
贪心策略
设各活动已按结束时间排序:f1≤f2≤…≤fn,先选出活动1,然后按活动编号从小到大的次序依次选择与当前活动相容的活动。
注:这种策略使剩余的可安排时间极大化,以便于安排尽可能多的相容活动。
ActivitySelection(n, s[], f[], a[]){
//f[]已排序,a[]记录选择的活动,即a[i]=true表示活动i已选择
a[1]=true;
j=1;
for i=2 to n do{
if(s[i]>=f[j]){
a[i]=true;
j=i;
}
else a[i]=false;
}
}
时间复杂度T(n)=O(nlogn)
示例三 最优装载
有一批集装箱要装上一艘载重量为C的轮船。其中集装箱i的重量为wi。最优装载问题要求确定在装载体积不受限制的情况下,将尽可能多的集装箱装上轮船。
贪心策略
从剩下的货箱中,选择重量最小的货箱。这种选择次序可以保证所选的货箱总重量最小,从而可以装载更多的货箱。根据这种贪婪策略,首先选择最轻的货箱,然后选次轻的货箱,如此下去直到所有货箱均装上船或船上不能再容纳其他任何一个货箱。
ContainerLoading(x[], w[], c, n){
//x[i] =1当且仅当货箱i被装载,对重量按间接寻址方式排序
new t[n+1]; //产生数组t,用于间接寻址
IndirectSort(w, t, n); //此时, w[t[i]]≤w[t[i+1]], 1≤i<n
for i = 1 to n do //初始化x
x[i] = 0;
for(i = 1; i <= n &&w[t[i]] <= c; i++) {
//按重量次序选择物品
x[t[i]] = 1;
c = c-w[t[i]];
} //c为剩余容量
delete t[]; //删除数组t
}
时间复杂度T(n)=O(nlogn)