贪心算法必知的知识点
贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,他所做出的是在某种意义上的局部最优解。
贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择,选择的贪心策略必须具备无后效性,即某个状态以前的过程不会影响以后的状态,只与当前状态有关。
基本思路
建立数学模型来描述问题;
把求解的问题分成若干个子问题;
对每一子问题求解,得到子问题的局部最优解;
把子问题的解局部最优解合成原来解问题的一个解。
案例分析
区间调度问题(interval shecduling)
问题描述:
这是《算法导论》上的例子,也是一个非常经典的问题。有n个需要在同一天使用同一个教室的活动a1,a2,…,an,教室同一时刻只能由一个活动使用。每个活动ai都有一个开始时间si和结束时间fi 。一旦被选择后,活动ai就占据半开时间区间[si,fi)。如果[si,fi]和[sj,fj]互不重叠,ai和aj两个活动就可以被安排在这一天。该问题就是要安排这些活动使得尽量多的活动能不冲突的举行。例如下图所示的活动集合S,其中各项活动按照结束时间单调递增排序。
几种贪心策略:
1.最早开始时间
即用每个工作的开始时间进行升序排列,在保证不冲突的情况下,每次选择开始时间最小的情况。
2.最早结束时间
即用每个工作的结束时间进行升序排列,在保证不冲突的情况下(仍然考虑下一次的结束时间尽可能的小,且下一次的开始时间需要大于上一次的结束时间),每次选择结束时间最小的情况。(上图)
3.最少的运行时间
即每一个工作的结束时间减去开始时间得到运行时间,并且在不冲突的情况下,每次选择运行时间最短的工作。
4.最少冲突情况
即首先计算出当前的工作i,它的冲突个数为k[i],用k[i]进行上升排序。每次选择冲突最小的情况。
哪种策略下能得到最优解?答:最早结束时间
证明方法:构造法(深灰表选中的工作,浅灰表示反例)
最早开始时间:假设最早开始时间的工作的运行时间很长,就不是最优。
最少运行时间:假设最少运行时间的的那个工作的结束时间很晚,就不是最优。
最少冲突:按上图的情况,首先会选到深灰,然后再选择旁边的浅灰,总共只有三个工作被选中,不是最优。
接着证明最早结束时间是最优的(可以用归纳法)
算法思想:
1.先按完成时间上升排序
2.选择当前的结束时间的最早的工作,判断该工作的开始时间是否满足大于上一个工作的结束时间
3.如果不满足,则寻找下一个,满足的话重复2步,就选择,直到遍历完
总结而言就是:先排序,不冲突就选择加入A(被选择的job),冲突就丢弃并遍历下一个,最后返回A
实现:https://www.jianshu.com/p/89f8c67b2a04
钱币找零问题
问题描述:这个问题在我们的日常生活中就更加普遍了。假设1元、2元、5元、10元、20元、50元、100元的纸币分别有c0, c1, c2, c3, c4, c5, c6张。现在要用这些钱来支付K元,至少要用多少张纸币?用贪心算法的思想,很显然,每一步尽可能用面值大的纸币即可。在日常生活中我们自然而然也是这么做的。
贪心策略:
最大面值:在程序中已经事先将Value按照从大到小的顺序排好。
算法思想:1.首先将面值从大到小排序 2.计算K/V的取低值为n,记录n,并求得剩余的K,重复2过程,直到K的取值为零。
背包问题
问题描述:有一个背包,背包容量是M=150。有7个物品,物品可以分割成任意大小。要求尽可能让装入背包中的物品总价值V最大,但不能超过总容量M。
贪心策略:
最大价值:在保证不超重的情况下,每次选择价值最大的物品。
结果:DBFE 不是最优
最小容量:在保证不超重的情况下,每次选择重量最小的物品。
结果:FGBAE 不是最优
最大单位容量的价值:选择单位容量下,价值最大的那一个。
结果:FBGD 40+40+30+50
算法思想:
1.按单位容量的价值由大到小排序
2.如果不超重,就选择,超重就舍弃。
实现:https://www.jianshu.com/p/50f1d4e0555c
最小生成树问题
问题描述:
求一个连通无向图的最小生成树的代价(图边权值为正整数)。
Kruskal算法简述
假设 WN=(V,{E}) 是一个含有 n 个顶点的连通网,则按照克鲁斯卡尔算法构造最小生成树的过程为:先构造一个只含 n 个顶点,而边集为空的子图,若将该子图中各个顶点看成是各棵树上的根结点,则它是一个含有 n 棵树的一个森林。之后,从网的边集 E 中选取一条权值最小的边,若该条边的两个顶点分属不同的树,则将其加入子图,也就是说,将这两个顶点分别所在的两棵树合成一棵树;反之,若该条边的两个顶点已落在同一棵树上,则不可取,而应该取下一条权值最小的边再试之。依次类推,直至森林中只有一棵树,也即子图中含有 n-1条边为止。
模拟过程:https://www.jianshu.com/p/50f1d4e0555c
算法难点:
(1)边的选择要求从小到大选择,则开始显然要对边进行升序排序。
(2)选择的边是否需要,则从判断该边加入后是否构成环入手。
算法设计:
(1)对边升序排序
在此采用链式结构,通过插入排序完成。每一结点存放一条边的左右端点序号、权值及后继结点指针
(2)边的加入是否构成环
一开始假定各顶点分别为一组,其组号为端点序号。选择某边后,看其两个端点是否在同一组中,即所在组号是否相同,如果是,表示构成了环,则舍去。 如果两个端点所在的组不同,则表示可以加入,则将该边两端的组合并成同一组。
参考:https://www.jianshu.com/p/50f1d4e0555c