顾名思义,贪心算法总是作出在当前看来最好的选择。也就是说贪心算法并不从整体最优考虑,它所作出的选择只是在某种意义上的局部最优选择
。当然,希望贪心算法得到的最终结果也是整体最优的。虽然贪心算法不能对所有问题都得到整体最优解,但对许多问题它能产生整体最优解。如单源最短路径问题,最小生成树问题等。在一些情况下,即使贪心算法不能得到整体最优解,其最终结果却是最优解的很好近似。
对于一个具体的问题,怎么知道是否可用贪心算法解此问题,以及能否得到问题的最优解呢?这个问题很难给予肯定的回答。
但是,从许多可以用贪心算法求解的问题中看到这类问题一般具有2个重要的性质:贪心选择性质和最优子结构性质
。
第一个基本要素
,也是贪心算法与动态规划算法的主要区别。必须证明每一步所作的贪心选择最终导致问题的整体最优解
。当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。问题的最优子结构性质是该问题可用动态规划算法或贪心算法求解的关键特征。
贪心算法和动态规划算法都要求问题具有最优子结构性质
,这是2类算法的一个共同点。但是,对于具有最优子结构的问题应该选用贪心算法还是动态规划算法求解?
设计要素:
贪心法的优势:算法简单,时间和空间复杂性低
数学归纳法
交换论证
设有n个活动的集合E={1,2,…,n},其中每个活动都要求使用同一资源,如演讲会场等,而在同一时间内只有一个活动能使用这一资源。每个活动i都有一个要求使用该资源的起始时间si和一个结束时间fi,且si <fi 。如果选择了活动i,则它在半开时间区间[si, fi)内占用资源。若区间[si, fi)与区间[sj, fj)不相交,则称活动i与活动j是相容的。也就是说,当si≥fj或sj≥fi时,活动i与活动j相容。
策略1:S={1,2,3},s1=0, f1=20, s2=2, f2=5, s3=8, f3=15
策略2:S={1,2,3},s1=0, f1=8, s2=7, f2=9, s3=8, f3=15
活动安排
Note:各活动的起始时间和结束时间存储于数组s和f中且按结束时间的非减序排列
public static int greedySelector(int[] s,int[] f,boolean[] a) {
int n=s.length;
a[0]=true;
int j=0;
int count=1;
for(int i=1;i<n;i++){
if(s[i]>=f[j]){
a[i]=true;
j=i;
count++;
} else {
a[i]=false;
}
}
return count;
}
例:设待安排的11个活动的开始时间和结束时间按结束时间的非减序排列如下:
算法greedySelector 的计算过程如左图所示。图中每行相应于算法的一次迭代。阴影长条表示的活动是已选入集合A的活动,而空白长条表示的活动是当前正在检查相容性的活动。
A = {1, 4, 8,11}
算法的正确性证明
定理算法Select 执行到第 k 步, 选择 k 项活动 i1= 1, i2, …, ik, 那么存在最优解 A 包含 i1=1, i2, … , ik .
根据定理:算法至多到第 n 步得到最优解
归纳步骤:假设命题对 k 为真, 证明对 k+1 也为真.
结束!