算法——贪心

Aha,又是新的一天,今天我们思考一下心算法。

作为佛教三毒之首的贪竟然也堂而皇之成为一种算法,供后人瞻仰礼拜。阿弥陀佛,罪过,罪过。之前的我是一直将这些东西视为封建教条一样束缚人的东西,慢慢的,从纳斯姐身上我开始理解原本存在于电影台词中的话语“人生就是一场修行”,这些妄念是需要在修行中克服掉的。

我呸,废话!

概念

贪心算法在对问题求解是,总是做出在当前看来最好的选择,也就是说不从全局考虑,而是从局部考虑,做出局部最优选择

贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择,选择的贪心策略必须具备无后效性,即某个状态以前的过程不会影响以后的状态,只与当前状态有关。

采用逐步构造最优的方法,在每个阶段都做出看上去最好的决策。

基本要素

贪心选择:整体最优解可以通过一系列局部最优的选择来达到,这是贪心与动态规划的主要区别。贪心选择是采用自顶向下、以迭代的方法做出相继选择,每做一次贪心选择就将所求问题简化为一个规模更小的子问题。

对于一个具体问题,要确定它是否具有贪心选择的性质,我们必须证明每一步所作的贪心选择最终能得到问题的最优解。通常可以首先证明问题的一个整体最优解,是从贪心选择开始的,而且作了贪心选择后,原问题简化为一个规模更小的类似子问题。然后,用数学归纳法证明,通过每一步贪心选择,最终可得到问题的一个整体最优解。

最优子结构:问题的最优解包含其子问题的最优解

与动态规划的差异

相同点:都具有最优子结构性质

不同点:动态规划每一步的解依赖子问题的解,而贪心算法不依赖子问题的解,只与当前状态有关。

贪心算法中作出的每步贪心决策都无法改变,因为贪心策略是由上一步的最优解推导下一步的最优解,而上一部之前的最优解则不作保留,贪心算法每一步的最优解一定包含上一步的最优解。动态规划算法中全局最优解中一定包含某个局部最优解,但不一定包含前一个局部最优解,因此需要记录之前的所有最优解。

贪心:自顶向下    动态规划:自底向上

基本思路

贪心算法的基本思路是从问题的某一个初始解出发一步一步地进行,根据某个优化测度,每一步都要确保能获得局部最优解。每一步只考虑一个数据,他的选取应该满足局部优化的条件。若下一个数据和部分最优解连在一起不再是可行解时,就不把该数据添加到部分解中,直到把所有数据枚举完,或者不能再添加算法停止。

(百度百科)

参考:https://blog.csdn.net/liufeng_king/article/details/8709005

例子

活动安排问题

问题描述:

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

分析:是否满足贪心算法的基本要素:首先要确实贪心准则,然后看是否满足两个要素:最优子结构、贪心选择。

贪心准则:在未安排的活动中选择结束时间最早的进行安排。需要首先对活动进行排序

代码实现:

Public static void greedySelector(int s[] , int f[],  bool a [])
{     int n=s.length-1;
       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;
}

非0-1揹包问题

问题描述:与动态规划中的揹包问题不同,这里的物品不是0-1的问题,也就是说,物品像面包一样可以切分成块。

分析:按照单位质量价值从大到小进行排序,进行贪心。算法的复杂度在于排序问题。

 

单源最短路径(Dijkstra算法解法)

问题描述:给定带权有向图G =(V,E),其中每条边的权是非负实数。另外,还给定V中的一个顶点,称为源。现在要计算从源到所有其它各顶点的最短路径长度。这里路径的长度是指路径上各边权之和。

参考:https://blog.csdn.net/qq_35644234/article/details/60870719https://blog.csdn.net/qq_39521554/article/details/79333690

分析:Dijkstra算法采用的是一种贪心的策略,声明一个数组dis来保存源点到各个顶点的最短距离和一个保存已经找到了最短路径的顶点的集合:T,初始时,原点 s 的路径权重被赋为 0 (dis[s] = 0)。若对于顶点 s 存在能直接到达的边(s,m),则把dis[m]设为w(s, m),同时把所有其他(s不能直接到达的)顶点的路径长度设为无穷大。初始时,集合T只有顶点s。然后,从dis数组选择最小值,则该值就是源点s到该值对应的顶点的最短路径,并且把该点加入到T中,此时完成一个顶点, 然后,我们需要看看新加入的顶点是否可以到达其他顶点并且看看通过该顶点到达其他点的路径长度是否比源点直接到达短,如果是,那么就替换这些顶点在dis中的值。 然后,又从dis中找出最小值,重复上述动作,直到T中包含了图的所有顶点。

不同于普通的贪心算法维护一个局部最优值,Dijkstra算法维护的局部最优值是一个数组

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章