算法——贪心算法

简介:

贪心算法

贪心算法通过一系列的选择来得到问题的解。它所做的每一个选择都是当前状态下局部的最好选择,即贪心选择。
贪心选择的一般特征:贪心选择性质和最优子结构性质。

贪心选择性质:

所谓贪心选择性质是指所求问题的整体最优解可以通过一系列局部最优的选择,即贪心选择来达到。这是贪心算法可行的第一个基本要素,也是贪心算法与动态规划算法的主要区别。在动态规划算法中,每步所做的选择往往依赖于相关子问题的解。因而只有在解出相关子问题后,才能做出选择。而在贪心算法中,仅在当前状态下做出最好选择,即局部最优选择。然后再去解出做出这个选择后产生的相应的子问题。贪心算法所做的贪心选择可以依赖于以往所做过的选择,但决不依赖于将来所做的选择,也不依赖于子问题的解。正是由于这种差别,动态规划算法通常以自底向上的方式解各个问题,而贪心算法则通常以自顶向下的方式进行,以迭代的方式做出相继的贪心选择,没做一次贪心选择就将所求问题简化为规模更小的子问题。
对于一个具体问题,要确定它是否具有贪心选择性质,必须证明每一步所做的贪心选择最终导致问题的整体最优解。

最优子结构性质:

当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。

弹性算法与动态规划算法的差异:

贪心算法和动态规划算法都要求问题具有最优子结构性质,这是两类算法的一个共同点。大多数时候,能用贪心算法求解的问题,都可以用动态规划算法求解。但是能用动态规划求解的,不一定能用贪心算法进行求解。

活动安排问题:

问题:

设有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相容。活动安排问题就是要在所给的活动集合中选择出最大的相容活动子集合。

关于证明:

这个问题可以使用贪心算法进行求解。其实这个问题的关键并不是如何用贪心算法求解,而是如何证明这个问题可以用贪心算法求解。鉴于证明的复杂性,还是不在此讨论证明问题。其实贪心算法问题的证明无非都是用数学归纳法证明,不错就是那个传说中万能证明法,数学归纳法。还是直接讨论实现过程吧。

实现:

首先将活动按照活动的结束时间非递减:F1<=F2<=...<=Fn排序。如果所给的活动未排序,则先将活动按此序排列,时间复杂度一般为O(nlogn)可解决。以下是解决问题的算法。

//贪心算法-活动安排的实现

#include "stdafx.h"
#include "stdio.h"

template<class Type>
void GreedySelector(int n,Type s[],Type f[],bool A[])
{
    A[0]=1;    //第一个直接选取
    int j=0;
    for(int i=1;i<n;i++)
    {
        if(s[i]>=f[j]){A[i]=true;j=i;}    //如果第i+1的活动的开始时间大于或等于第i个活动的结束时间,则标记该活动
        else A[i]=false;
    }
}

int _tmain(int argc, _TCHAR* argv[])
{
    //初始化数据
    int n=3;
    int s[3]={1,2,4};    //开始时间
    int f[3]={3,3,5};    //结束时间
    bool A[3]={0,0,0};    //数组A用于标记是否选择活动,1表示选择,0表示不选择

    GreedySelector<int>(n,s,f,A);
    for(int i=0;i<n;i++)
    {
        printf("%d\n",A[i]);
    }
}


该算法的贪心选择的意义是使剩余的可安排的时间段极大化,以便安排尽可能多的相容活动。也就是说,每次选择完了之后,保证这次的选择之后留下的时间是最多的。

测试结果

时间复杂度:

GreedySelector算法效率极高。当输入的数据是已经按照结束时间非递减排序好的时候,算法只需要O(n)的时间安排n个活动,使最多的活动能相容地使用公共资源。

适用范围:

贪心算法并不能总求得问题的整体最优解。但对于某些问题,却总能求得整体最优解,这要看问题时什么了。只要能满足贪心算法的两个性质:贪心选择性质和最优子结构性质,贪心算法就可以出色地求出问题的整体最优解。即使某些问题,贪心算法不能求得整体的最优解,贪心算法也能求出大概的整体最优解。如果你的要求不是太高,贪心算法是一个很好的选择。最优子结构性质是比较容易看出来的,但是贪心选择性质就没那么容易了,这个时候需要证明。证明往往使用数学归纳法。

适用范围个人总结的一句话,能证明用就可以用。(因为个人觉得证明比算法实现还难)

 

参考资料:

计算机算法设计与分析/王晓东编著。-3版。-北京:电子工业出版社,2007.5

相关文章
|
6月前
|
机器学习/深度学习 算法 测试技术
【动态规划】【C++算法】2742. 给墙壁刷油漆
【动态规划】【C++算法】2742. 给墙壁刷油漆
|
算法 数据挖掘 调度
【算法分析与设计】贪心算法(下)
【算法分析与设计】贪心算法(下)
【算法分析与设计】贪心算法(下)
|
移动开发 算法 调度
【贪心算法】一文让你学会“贪心”(贪心算法详解及经典案例)
贪心算法是一种非常常见的算法,它的简单和高效性使其在实际应用中被广泛使用。 贪心算法的核心思想是在每一步都采取当前状态下最优的选择,而不考虑未来可能产生的影响。虽然贪心算法不能保证总是得到最优解,但在很多情况下,它可以获得很好的结果。 本篇文章将介绍贪心算法的基本概念和一些经典应用,以及如何通过贪心算法来解决一些实际问题。希望通过本文的阅读,读者可以对贪心算法有更加深刻的理解,并能够在实际问题中应用贪心算法来得到更好的解决方案。 让我们暴打贪心算法吧!
3199 0
|
1月前
|
存储 算法
算法之背包问题
本文讨论了可分背包问题和0-1背包问题的区别及解决方法,其中可分背包问题可以使用贪心算法解决,而0-1背包问题则通常采用动态规划方法来找到最大价值的解决方案。
40 0
算法之背包问题
|
3月前
|
算法 Java 测试技术
算法设计(动态规划实验报告) 基于动态规划的背包问题、Warshall算法和Floyd算法
这篇文章介绍了基于动态规划法的三种算法:解决背包问题的递归和自底向上实现、Warshall算法和Floyd算法,并提供了它们的伪代码、Java源代码实现以及时间效率分析。
算法设计(动态规划实验报告) 基于动态规划的背包问题、Warshall算法和Floyd算法
|
5月前
|
存储 算法
动态规划与搜索算法
动态规划与搜索算法
75 0
|
6月前
|
算法 调度 C语言
算法--贪心算法
贪心算法是每次选择当前最优解,期望达到全局最优,适用于有最优子结构的问题。它不回退,与动态规划不同。适用于分数背包问题,但0-1背包问题可能无法保证最优解。常见应用包括找零、最小生成树、单源最短路径和任务调度。贪心算法步骤包括建模、分问题、求局部最优解及整合。尽管简单,但需谨慎评估是否适用,例如0-1背包问题可能需用动态规划求解。
61 1
|
6月前
|
人工智能 算法 Java
【算法设计与分析】— —单源最短路径的贪心算法
【算法设计与分析】— —单源最短路径的贪心算法
|
6月前
|
算法 C++
【动态规划】【C++算法】956 最高的广告牌
【动态规划】【C++算法】956 最高的广告牌
|
11月前
|
算法
【算法】动态规划
【算法】动态规划
74 0
下一篇
无影云桌面