贪心算法

本文详细介绍了贪心算法的基本思想、适用特征及证明方法,通过活动安排、最优装载、最优前缀码(哈夫曼编码)、最小生成树和单源最短路径等经典问题,展示了贪心算法在解决实际问题中的应用。同时,针对多级调度问题,虽然它是NP完全问题,但贪心策略仍能提供较好的近似解。贪心算法的关键在于局部最优选择的累积能够导出全局最优解,对于特定问题类型,贪心法能够高效地找到解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

贪心算法

基本思想

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

适用特征

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

证明方法

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

总结

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

例题

1.活动安排

问题

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

S={1,2,...,n}S=\{1,2,...,n\}S={1,2,...,n}为活动的集合,sis_isifif_ifi分别为活动i的开始和截止时间,i=1,2,...,ni=1,2,...,ni=1,2,...,n。定义

活动i与j相容:si≥fjs_i\geq f_jsifjsj≥fi,i≠js_j \geq f_i,i \neq jsjfi,i=j

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

思想

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

把活动按照截止时间从小到大安排,使得f1≤f2≤...≤fnf_1 \leq f_2 \leq ... \leq f_nf1f2...fn,然后从前向后挑选,只要与前面选择的活动相容,便将这项活动选入最大相容集合AAA

代码

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的重量为wiw_iwi。最优转载问题要求确定在装载体积不受限制的情况下,将尽可能多的集装箱装上轮船。

思路

贪心策略:轻者先装

正确性证明

对问题规模进行归纳

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

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

问题

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

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

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

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

思路

f(ci)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^{'}GGGG的生成树。生成树上各边权的总和称为该生成树的耗费。在G的所有生成树中,耗费最小的生成树称为G的最小生成树

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

思路

  • PrimPrimPrim算法

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

  • KruskalKruskalKruskal算法

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

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

5.单源最短路径

问题

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

思路

DijkstraDijkstraDijkstra算法

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

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

6.多级调度问题

问题

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

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

思路

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

愤怒的卤蛋

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

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

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

打赏作者

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

抵扣说明:

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

余额充值