贪心算法

贪心算法

基本思想

贪心算法总是作出在当前看来最好的选择。也就是说贪心算法并不从整体最优考虑,它所做出的选择只是在某种意义上的局部最优选择。

适用特征

  • 最优子结构性质
  • 贪心选择性质:所求问题的整体最优解可以通过一系列局部最优的选择,即贪心选择来达到。对于一个具体问题,要确定它是否具有贪心选择性质,必须证明每一步所做的贪心选择最终导致问题的整体最优解。

证明方法

  • 对算法步数的归纳
  • 对问题规模的归纳

总结

  • 贪心法需要正确性证明
  • 贪心法一般需要对原始数据预处理(排序)
  • 程序结构一般自顶向下,一次扫描

例题

1.活动安排

问题

有n个活动申请使用同一个礼堂,每项活动有一个开始时间和一个截止时间,如果任何两个活动不能同时进行,问如何选择这些活动,从而使得被安排的活动数量达到最多。

S = { 1 , 2 , . . . , n } S=\{1,2,...,n\} S={1,2,...,n}为活动的集合, s i s_i si f i f_i fi分别为活动i的开始和截止时间, i = 1 , 2 , . . . , n i=1,2,...,n i=1,2,...,n。定义

活动i与j相容: s i ≥ f j s_i\geq f_j sifj s j ≥ f i , i ≠ j s_j \geq f_i,i \neq j sjfi,i=j

求S最大的两两相容的活动子集

思想

贪心策略:早完成的活动先安排

把活动按照截止时间从小到大安排,使得 f 1 ≤ f 2 ≤ . . . ≤ f n f_1 \leq f_2 \leq ... \leq f_n f1f2...fn,然后从前向后挑选,只要与前面选择的活动相容,便将这项活动选入最大相容集合 A A A

代码

template<class Type>
void GreedSelector(int n,Type s[],Type f[],bool A[]){//已排好序
    A[1] = true;
    int j= 1;
    for(int i=2;i<=n;i++){
        if(s[i]>=f[i]){
            A[i] = true;
            j = i;
        }
        else{
            A[i] = false;
        }
    }
}

2.最优装载

问题

有一批集装箱要装上一艘载重量为c的轮船。其中集装箱i的重量为 w i w_i wi。最优转载问题要求确定在装载体积不受限制的情况下,将尽可能多的集装箱装上轮船。

思路

贪心策略:轻者先装

正确性证明

对问题规模进行归纳

  • 设集装箱已按重量从小到大排序,标号为 1 , 2 , . . . , n 1,2,...,n 1,2,...,n
  • k=1,只有一个集装箱,装 ( w 1 ≤ c ) (w_1 \le c) (w1c)或不装 ( w 1 ≥ c ) (w_1 \ge c) (w1c),算法得到最优解 I 1 I_1 I1
  • 假设算法对于规模为n-1的输入算法得到最优解,则对于 N ′ = { 2 , 3 , . . . , n } , c ′ = c − w i N^{'}=\{2,3,...,n\},c^{'}=c-w_i N={2,3,...,n},c=cwi,算法得到最优解 I ′ I^{'} I
  • 对于规模n的输入 N = { 1 , 2 , . . . , n } N=\{1,2,...,n\} N={1,2,...,n},其中 w 1 ≤ w 2 ≤ . . . ≤ w n w_1 \le w_2 \le ... \le w_n w1w2...wn。则 I = I ′ ⋃ I 1 I=I^{'} \bigcup I_1 I=II1为N的最优解。若最优解为 I ∗ I^* I,且 I ∗ I^* I中不包含 I 1 I_1 I1,则用 I 1 I_1 I1替换 I ∗ I^* I中标号最小的集装箱得到的解也为最优解

3.最优前缀码[哈夫曼编码]

问题

给出现频率高的字符较短的编码,出现频率较低的字符以较长的编码,可以大大缩短总码长

对每一个字符规定一个 0 , 1 0,1 0,1串作为其代码,并要求任一字符的代码都不是其他字符代码的前缀。这种编码称为前缀码。

给定编码字符集 C C C及其频率分布 f f f,即 C C C中任一字符c以频率 f ( c ) f(c) f(c)在文件中出现。 C C C中一个前缀编码方案对应一个二叉树T,字符c在T中的深度记为 d T ( c ) d_T(c) dT(c), d T ( c ) d_T(c) dT(c)也是字符c的前缀码长。

给定字符集 C = { c 1 , c 2 , . . . , c n } C=\{c_1,c_2,...,c_n\} C={c1,c2,...,cn}以及每个字符频率 f ( c i ) f(c_i) f(ci),求关于C的一个最优前缀编码

思路

f ( c i ) f(c_i) f(ci)按照概率大从小到大排序 ,按顺序两两合并运算后继续合并

4.最小生成树

问题

G = ( V , E ) G=(V,E) G=(V,E)是无向连通带权图,即一个网络。E中每条边 ( u , v ) (u,v) (u,v)的权为 w [ u ] [ v ] w[u][v] w[u][v]。如果G的子图 G ′ G^{'} G是一颗包含G的所有顶点的树,则称 G ′ G^{'} G G G G的生成树。生成树上各边权的总和称为该生成树的耗费。在G的所有生成树中,耗费最小的生成树称为G的最小生成树

给定无向连通带权图 G ( V , E , W ) G(V,E,W) G(V,E,W),求 W ( T ) W(T) W(T)最小的生成树

思路

  • P r i m Prim Prim算法

    首先置 S = { 1 } S=\{1\} S={1},然后只要S是V的真子集,就做如下的贪心选择:选取满足条件 i ∈ S , j ∈ V − S i \in S,j \in V-S iS,jVS,且 w [ i ] [ j ] w[i][j] w[i][j]最小的边,将定点j添加到S中。这个过程一直进行到S=V

  • K r u s k a l Kruskal Kruskal算法

    首先将G的n个顶点看成n个孤立的连通分支。将所有的边按权从小到大排序

    然后按照权值从小到大添加,但是不能形成圈

5.单源最短路径

问题

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

思路

D i j k s t r a Dijkstra Dijkstra算法

设置顶点集合S并不断做贪心选择来扩充这个集合。一个顶点属于集合S当且仅当从源到该顶点的最短路径长度已知。

初始时,S中仅含有源。设u是G的某一个顶点,把从源到u且中间只经过S中顶点的路称为从源到u的特殊路径,并用数组 d i s t dist dist记录当前每个顶点所对应的最短特殊路径长度。 D i j k s t r a Dijkstra Dijkstra算法每次从V-S中取出具有最短特殊路径长度的顶点u,将u添加到S中,同时对数组 d i s t dist dist做必要的修改。一旦S包含了所有V中顶点, d i s t dist dist就记录了从源到所有其他顶点之间的最短路径长度。

6.多级调度问题

问题

设有n个独立的作业 { 1 , 2 , . . . , n } \{1,2,...,n\} {1,2,...,n},由m台相同的机器进行加工处理,作业i的处理时间为 t i t_i ti,任何作业不可以拆分为更小作业,且作业未完工前不允许中断处理。

多级调度问题要求给出一种作业调度方案,使所给的n个作业在尽可能短的时间内由m台机器加工处理完成。

思路

NP完全问题,到目前为止没有有效解法。这里用贪心选择策略设计出较好的近似算法。

采用最长处理时间作业优先的贪心选择策略

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
国科大的算法设计与分析相关1-5章复习题 第一章样例: 1.讲义习题一: 第1(执行步改为关键操作数)、第2、3、6、7题 习题一 1答:执行步4pmn+3pm+2m+1;关键操作2n*m*p 2方法一答:2n-2次 方法二答:2n-2次 3 1)证明:任给c,n>c,则10n2>cn 。不存在c使10n22c时,logn>c,从而n2logn>=cn2,同上。 6 答:logn,n2/3,20n,4n2,3n,n! 7 答:1)6+n 2) 3)任意n 2.讲义习题二:第5题。 答:c、e是割点。每点的DFN、L值:A1,1、B2,1、C3,1、D4,4、E5,1、F6,5、G7,5。最大连通分支CD、EFG、ABCE。 3.考虑下述选择排序算法: 输入:n个不等的整数的数组A[1..n] 输出:按递增次序排序的A For i:=1 to n-1 For j:=i+1 to n If A[j]<A[i] then A[i] A[j] 问:(1)最坏情况下做多少次比较运算?答1+2+..+n-1=n(n-1)/2 (2)最坏情况下做多少次交换运算?在什么输入时发生? n(n-1)/2,每次比较都交换,交换次数n(n-1)/2。 4.考虑下面的每对函数f(n)和g(n) ,比较他们的阶。 (1) f(n)=(n2-n)/2, g(n)=6n (2)f(n)=n+2 , g(n)=n2 (3)f(n)=n+nlogn, g(n)=n (4)f(n)=log(n!), g(n)= 答:(1)g(n)=O(f(n)) (2)f(n)=O(g(n) (3)f(n)=O(g(n) (4)f(n)=O(g(n) 5.在表中填入true或false . 答案: f(n) g(n) f(n)=O(g(n) f(n)=(g(n)) f(n)=(g(n)) 1 2n3+3n 100n2+2n+100 F T F 2 50n+logn 10n+loglogn T T T 3 50nlogn 10nloglogn F T F 4 logn Log2n T F F 5 n! 5n F T F 6.用迭代法求解下列递推方程: (1) (2) ,n=2k 答:(1)T(n)=T(n-1)+n-1=T(n-2)+n-2+n-1 =…=T(1)+1+2+…+n-1=n(n-1)/2=O(n2) (2)T(n)=2T(n/2)+n-1=2(2T(n/4)+n/2-1)+n-1 =4T(n/4)+n-2+n-1=4(2T(n/23)+n/4-1)+n-2+n-1 =23T(n/23)+n-4+n-2+n-1

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

愤怒的卤蛋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值