算法设计与分析第四章:贪心算法

算法设计与分析第四章:贪心算法_第1张图片

算法的基本要素:

最优子结构性质:

当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。问题的最优子结构性质是该问题可用动态规划算法或贪心算法求解的关键特征。

贪心选择性质:

所谓贪心选择性质是指所求问题的整体最优解可以通过一系列局部最优的选择,即贪心选择来达到。这是贪心算法可行的第一个基本要素,也是贪心算法与动态规划算法的主要区别。

动态规划算法通常以自底向上的方式解各子问题,而贪心算法则通常以自顶向下的方式进行,以迭代的方式作出相继的贪心选择,每作一次贪心选择就将所求问题简化为规模更小的子问题。

对于一个具体问题,要确定它是否具有贪心选择性质,必须证明每一步所作的贪心选择最终导致问题的整体最优解。

暴力搜素、动态规划、贪心 的区别:

搜索:遍历每一条可能的分支
动态规划:记忆化搜索,重叠子问题只计算一次
贪心:不解决所有可能的子问题,贪心地选择其中一个
动态规划就是暴力搜索加缓存(记忆化搜索),暴力搜素是自顶向下,动态规划是自低向上递推。

贪心是求局部最优,以得到全局最优(不一定是正确的,需要证明) 
dp是通过一些状态来描述一些子问题,然后通过状态之间的转移来求解(一般只要转移方程是正确的,答案必然是正确的)

应用范例:

活动安排问题

所有活动按照结束时间非递减排序,一开始选择活动 1 ,然后依次检查活动 i 是否与当前已选择的所有活动相容。若相容,则将活动i加入到已选择的集合 A 中;否则不选择活动i,继续检查下一活动与集合 A 中活动的相容性。

最优装载问题

采用重量最轻者先装的贪心选择策略,可产生最优装载问题的最优解。

哈夫曼编码

哈夫曼算法以自底向上的方式构造表示最优前缀码的二叉树 T ,算法以 | C | 个叶节点开始,执行 | C | - 1 次的 ” 合并 “ 运算后产生最终所要求的树 T 。设编码字符集中每个字符 c 的频率是 f ( c ) 。以 f 为键值的优先队列 Q 用在做贪心选择时有效确定算法当前要合并的两棵具有最小频率的树。一旦两棵具有最小频率的树合并后,产生一棵新的树,其频率为合并的两棵树的频率之和,并将新树插入优先队列 Q 。

单元最短路径

Dijistra算法:

设置顶点集合 S ,不断地做贪心选择来扩充这个集合。一个顶点属于集合 S 当且仅当从源到该顶点的最短路径长度已知。初始时,S 中仅有源。设 u 是 G 的某一个顶点,把从源到 u 且中间只经过 S 中顶点的路称为从源到 u 的特殊路径,并用数组 dist 记录当前每个顶点所对应的最短特殊路径长度。Dijkstra 算法每次从 V - S 中取出具有最短特殊路径长度的顶点 u ,将 u 添加到 S 中,同时对数组 dist 做必要的修改。一旦 S 包含了所有 V 中顶点,dist 就记录了从源到所有其他顶点之间的最短路径长度。

最小生成树

 Prim算法 :

设 G = ( V , E ) 是连通带权图,V= { 1 , 2 , … , n } 。构造 G 的最小生成树的 Prim 算法基本思想:首先置 S = { 1 } ,然后,只要 S 是 V 的真子集,就做如下贪心选择:选取满足条件 i ∈ S , j ∈ V - S ,且 c [ i ] [ j ] 最小的边 ( c [ i ] [ j ] 表示边 ( i , j ) 的权值 ) ,并将顶点 j 添 加到 S 中。这个过程一直进行到 S = V 为止,选取到的所有边恰好构成 G 的一棵最小生成树。

 Kruskal算法 :

设 G = ( V , E ) 是无向连通带权图,V= { 1 , 2 , … , n } 。构造 G 的最小生成树的 Kruskal 算法基本思想:首先将 G 的 n 个顶点看成 n 个孤立的连通分支,将所有的边按权从小到大排序;然后从第一条边开始,依次权递增的顺序查看每条边,并按下述方法连接两个不同的连通分支:当查看到第 k 条边 ( v , w ) 时,如果端点 v 和 w 分别是当前两个不同的连通分支 T1 和 T2 中的顶点时,就用边 ( v , w ) 将 T1 和 T2 连接成一个连通分支,然后继续查看第 k + 1 条边。这个过程一直进行到只剩下一个连通分支时为止。此时,这个连通分支就是 G 的一棵最小生成树。

多机调度问题

采用最长处理时间作业优先的贪心选择策略,可以设计出解多机调度问题的较好近似算法。按此策略:
当 n <= m 时,只要将机器 i 的 [ 0 , ti ] 时间区间分配给作业i即可。
当 n > m 时,先将 n 个作业依其所需的处理时间从大到小排序,再依此顺序将作业分配给空闲的机器。

区间覆盖

对所有点按照坐标从小到大排序,将第一个点设为区间起点,然后向后枚举,若当前点的距离与区间起点距离小于等于区间长度,就继续枚举,否则将此点设为新的区间起点,然后继续上述过程。若所有点都被覆盖则程序结束。

会场安排问题

对所有活动按照开始时间非递减排序。遍历所有活动,首先选第一个进入集合 A ,然后对于活动 i , 若 i 的开始时间比集合 A 的最晚时间要晚,那么就将 i 加入集合A,否则继续看下一个活动是否满足上述条件。遍历结束,集合A中的所有活动都安置在同一会场,然后清空集合 A ( 表示新开一个会场 ) ,再次进行遍历活动,之前安置过的活动不再进入集合 A 。当所有活动都被安置,此时的会场数就是最优解。

你可能感兴趣的:(经验分享,c++,算法,贪心算法)