算法设计与分析 授课教师:王秋芬 办公地点:7307

Slides:



Advertisements
Similar presentations
因数与倍数 2 、 5 的倍数的特征
Advertisements

2.8 函数的微分 1 微分的定义 2 微分的几何意义 3 微分公式与微分运算法则 4 微分在近似计算中的应用.
2.5 函数的微分 一、问题的提出 二、微分的定义 三、可微的条件 四、微分的几何意义 五、微分的求法 六、小结.
全微分 教学目的:全微分的有关概念和意义 教学重点:全微分的计算和应用 教学难点:全微分应用于近似计算.
练一练: 在数轴上画出表示下列各数的点, 并指出这些点相互间的关系: -6 , 6 , -3 , 3 , -1.5, 1.5.
常用逻辑用语复习课 李娟.
小学生游戏.
最大团问题 回溯法应用 作者:余新华 时间:
第5章 定积分及其应用 基本要求 5.1 定积分的概念与性质 5.2 微积分基本公式 5.3 定积分的换元积分法与分部积分法
§5 微分及其应用 一、微分的概念 实例:正方形金属薄片受热后面积的改变量..
Examples for transfer function
2-7、函数的微分 教学要求 教学要点.
§5 微分及其应用 一、微分的概念 实例:正方形金属薄片受热后面积的改变量..
探索三角形相似的条件(2).
初中数学八年级下册 (苏科版) 10.4 探索三角形 相似的条件(2).
C++中的声音处理 在传统Turbo C环境中,如果想用C语言控制电脑发声,可以用Sound函数。在VC6.6环境中如果想控制电脑发声则采用Beep函数。原型为: Beep(频率,持续时间) , 单位毫秒 暂停程序执行使用Sleep函数 Sleep(持续时间), 单位毫秒 引用这两个函数时,必须包含头文件
在PHP和MYSQL中实现完美的中文显示
树和二叉树(四).
强连通分量 无向图 1、任意两顶点连通称该图为连通图 2、否则将其中的极大连通子图称为连通分量 A D C B E 有向图
第七章 图 东南大学计算机学院 方效林 本课件借鉴了清华大学殷人昆老师 和哈尔滨工业大学张岩老师的课件.
第七章 图.
What have we learned?.
动态规划(Dynamic Programming)
使用矩阵表示 最小生成树算法.
无向树和根树.
第4章 非线性规划 4.5 约束最优化方法 2019/4/6 山东大学 软件学院.
数列.
知识点回顾 拓扑排序 关键路径 应用领域、求解步骤、算法实现过程 应用领域 求解步骤
算法设计与分析 ——贪心法. 算法设计与分析 ——贪心法 贪心算法 主要内容: 介绍贪心法的基本原理,贪心算法设计的基本方法和应用例子 。
顺序表的删除.
线性规 Linear Programming
概 率 统 计 主讲教师 叶宏 山东大学数学院.
Three stability circuits analysis with TINA-TI
WPT MRC. WPT MRC 由题目引出的几个问题 1.做MRC-WPT的多了,与其他文章的区别是什么? 2.Charging Control的手段是什么? 3.Power Reigon是什么东西?
复习.
用计算器开方.
成绩是怎么算出来的? 16级第一学期半期考试成绩 班级 姓名 语文 数学 英语 政治 历史 地理 物理 化学 生物 总分 1 张三1 115
正切函数的图象和性质 周期函数定义: 一般地,对于函数 (x),如果存在一个非零常数T,使得当x取定义域内的每一个值时,都有
用穷举法设计程序 南京师范大学 教育技术系 倪佳慧
§6.7 子空间的直和 一、直和的定义 二、直和的判定 三、多个子空间的直和.
知识点回顾 图的深度优先和广度优先遍历思想 图的深度优先遍历算法(用邻接表作为存储结构) 图的广度优先遍历算法(用邻接矩阵作为存储结构)
3.1.2 空间向量的数量积运算 1.了解空间向量夹角的概念及表示方法. 2.掌握空间向量数量积的计算方法及应用.
1.2 子集、补集、全集习题课.
1.设A和B是集合,证明:A=B当且仅当A∩B=A∪B
树和图 tree and graph 蔡亚星.
第三章 函数的微分学 第二节 导数的四则运算法则 一、导数的四则运算 二、偏导数的求法.
第4课时 绝对值.
多层循环 Private Sub Command1_Click() Dim i As Integer, j As Integer
第七、八次实验要求.
Models and Software Practice of the Operations Research
树和二叉树(四).
上杭二中 曾庆华 上杭二中 曾庆华 上杭二中 曾庆华.
第15讲 特征值与特征向量的性质 主要内容:特征值与特征向量的性质.
2.2直接证明(一) 分析法 综合法.
正弦、余弦函数的性质 华容一中 伍立华 2017年2月24日.
生 物 信 息 学 Bioinformatics 巩晶 癌症研究中心 山东大学 医学院
动态规划 Floyd最短路径算法 高文宇
最小生成树.
基于列存储的RDF数据管理 朱敏
第四节 向量的乘积 一、两向量的数量积 二、两向量的向量积.
4.2 图的存储结构(cont.) v1 v2 v3 v4 v5 vertex= v1 v2 v3 v4 v5 v
线性规划 Linear Programming
第十七讲 密码执行(1).
插入排序的正确性证明 以及各种改进方法.
位似.
使用Fragment 本讲大纲: 1、创建Fragment 2、在Activity中添加Fragment
1、图的基本概念 2、图的存储结构 3、图的遍历与连通性
最小生成树 最优二叉树.
§2 自由代数 定义19.7:设X是集合,G是一个T-代数,为X到G的函数,若对每个T-代数A和X到A的函数,都存在唯一的G到A的同态映射,使得=,则称G(更严格的说是(G,))是生成集X上的自由T-代数。X中的元素称为生成元。 A变, 变 变, 也变 对给定的 和A,是唯一的.
3.3.2 两点间的距离 山东省临沂第一中学.
Presentation transcript:

算法设计与分析 授课教师:王秋芬 办公地点:7307 Email: w_qiufen@163.com

第二章 贪心法 目录 概述 会场安排问题 单源最短路径问题 哈夫曼编码 最小生成树

教学目标 理解贪心法的概念 掌握贪心法的基本思想和要素 理解贪心法的正确性证明方法 通过对实例的学习,掌握贪心法的设计策略及解题步骤

学习贪心法的实际意义和学术价值 贪心法可以算得上是最接近人们日常思维的一种解题策略 简单、直接和高效 对范围相当广泛的许多实际问题它通常都能产生整体最优解,在一些情况下,即使采用贪心法不能得到整体最优解,但其最终结果却是最优解的很好近似解。 基于此,它在对NP完全问题的求解中发挥着越来越重要的作用。另外,近年来贪心法在各级各类信息学竞赛、ACM程序设计竞赛中经常出现。

贪心法的基本思想 基本思想 从问题的某一个初始解出发,在每一个阶段都根据贪心策略来做出当前最优的决策,逐步逼近给定的目标,尽可能快地求得更好的解。当达到算法中的某一步不能再继续前进时,算法终止。 得出的结论 每个阶段面临选择时, 贪心法都做出对眼前来讲是最有利的选择 选择一旦做出不可更改,即不允许回溯 根据贪心策略来逐步构造问题的解

举例 找零钱问题 (10元、5元、1元、5角、2角、1角、 )找零57.8元 背包问题 0-1背包问题 竞争资源安排问题 4角 (10元、5元、1元、5角、2角、1角、 )找零57.8元 背包问题 n=3,C=20,(w1,w2,w3)=(18,15,10),(v1,v2,v3)=(25,24,15) 0-1背包问题 竞争资源安排问题 4个班级争用教室A,尽可能满足更多班级的需要。 1班:8:00-12:00 上课 2班:8:30-10:30 讲座 3班:11:00-11:30 开会 4班:10:40-11:20 活动 4角

贪心法的基本要素 最优子结构性质 一个问题的最优解一定包含其子问题的最优解 采用反证法证明 贪心选择性质 所求问题的整体最优解可以通过一系列局部最优的选择获得,即通过一系列的逐步局部最优选择使得最终的选择方案是全局最优的

贪心法的解题步骤 分解:将原问题分解为若干个相互独立的阶段。 解决:对于每个阶段依据贪心策略进行贪心选择,求出局部的最优解。 合并:将各个阶段的解合并为原问题的一个可行解。影响时间复杂性的因素

会场安排问题 问题描述 设有n个会议的集合C={1,2,…,n},其中每个会议都要求使用同一个资源(如会议室),而在同一时间内只能有一个会议使用该资源。每个会议i都有要求使用该资源的起始时间bi和结束时间ei,且bi < ei 。如果选择了会议i使用会议室,则它在半开区间[bi, ei)内占用该资源。如果[bi, ei)与[bj , ej)不相交,则称会议i与会议j是相容的。会场安排问题要求在所给的会议集合中选出最大的相容活动子集,也即尽可能地选择更多的会议来使用资源。

贪心策略 选择最早开始时间且不与已安排会议重叠的会议 选择使用时间最短且不与已安排会议重叠的会议 选择具有最早结束时间且不与已安排会议重叠的会议

算法设计 步骤1:初始化。开始时间存B;结束时间存E中且按照结束时间的非减序排序,B相应调整;集合A存储解,会议i如果在集合A中,当且仅当A[i]=true; 步骤2:根据贪心策略,首令A[1]=true; 步骤3:依次扫描每一个会议,如果会议i的开始时间不小于最后一个选入A中的会议的结束时间,则将会议i加入A中;否则,放弃,继续检查下一个会议与A中会议的相容性。

示例 【例2-1】设有11个会议等待安排,用贪心法找出满足目标要求的会议集合。这些会议按结束时间的非减序排列如表2-1所示。 11个会议按结束时间的非减序排列表: 会议i 1 2 3 4 5 6 7 8 9 10 11 开始时间bi 12 结束时间ei 13 14 会议集合为{1,4,8,11}

算法正确性证明 问题存在从贪心选择开始的最优解 贪心选择性质 采用归纳法 一步一步的贪心选择能够得到问题的最优解 最优子结构性质 采用反证法

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

Dijkstra算法思想 按各个顶点与源点之间路径长度的递增次序,生成源点到各个顶点的最短路径的方法,即先求出长度最短的一条路径,再参照它求出长度次短的一条路径,依此类推,直到从源点到其它各个顶点的最短路径全部求出为止。

算法设计 u:源点。集合S中的顶点到源点的最短路径的长度已经确定,集合V-S中所包含的顶点到源点的最短路径的长度待定。 贪心策略:选择特殊路径长度最短的路径,将其相连的V-S中的顶点加入到集合S中。

求解步骤 步骤1:设计合适的数据结构。带权邻接矩阵C,即如果< u, x >E,令C[u][x]=<u, x >的权值,否则,C[u][x]=无穷;采用数组dist来记录从源点到其它顶点的最短路径长度;采用数组p来记录最短路径; 步骤2:初始化。令集合S={u},对于集合V-S中的所有顶点x,设置dist[x]=C[u][x];如果顶点i与源点相邻,设置p[i]=u,否则p[i]=-1; 步骤3:在集合V-S中依照贪心策略来寻找使得dist[x]具有最小值的顶点t,t就是集合V-S中距离源点u最近的顶点。 步骤4:将顶点t加入集合S中,同时更新集合V-S; 步骤5:如果集合V-S为空,算法结束;否则,转步骤6; 步骤6:对集合V-S中的所有与顶点t相邻的顶点x,如果dist[x]>dist[t]+C[t][x],则dist[x]=dist[t]+C[t][x]并设置p[x]=t。转步骤3。

构造实例 【例2-2】在如图2-2所示的有向带权图中,求源点0到其余顶点的最短路径及最短路径长度。 图2-2 有向带权图

实例的求解过程 S V-S dist[1] dist[2] dist[3] dist[4] t 初始 {0} {1,2,3,4} 8 32 max 1 {0,1} {2,3,4} - 24 23 3 2 {0,1,3} {2,4} 30 {0,1,3,2} {4} 4 {0,1,3,2,4} {}

前驱数组P变化情况表 1 2 3 4 初始 -1 依据此表,求得从源点0到顶点4的最短路径为0,1,3,4;从源点0到顶点3的最短路径为:0,1,3;而源点0到顶点2的最短路径为0,1,2;源点0到顶点1的最短路径为0,1。

算法描述及分析 从算法的描述中,不难发现语句if((!s[j])&&(dist[j]<temp))对算法的运行时间贡献最大,因此选择将该语句作为基本语句。当外层循环标号为1时,该语句在内层循环的控制下,共执行n次,外层循环从1~n,因此,该语句的执行次数为n*n=n2,算法的时间复杂性为O(n2)。 实现该算法所需的辅助空间包含为数组s和变量i、j、t和temp所分配的空间,因此,Dijkstra算法的空间复杂性为O(n)。

哈夫曼编码 哈夫曼编码问题的引出 不等长编码方法出现的问题:任何一个字符的编码都不能是其它字符编码的前缀(即前缀码特性),否则译码时将产生二义性。那么如何来设计前缀编码呢?利用二叉树来进行设计。具体做法是:约定在二叉树中用叶子结点表示字符,从根结点到叶子结点的路径中,左分支表示“0”,右分支表示“1”。那么,从根结点到叶子结点的路径分支所组成的字符串做为该叶子结点字符的编码,可以证明这样的编码一定是前缀编码,这棵二叉树即为编码树。 剩下的问题是怎样保证这样的编码树所得到的编码总长度最小?哈夫曼提出了解决该问题的方法,由此产生的编码方案称为哈夫曼算法。

算法的构造思想 以字符的使用频率做权构建一棵哈夫曼树,然后利用哈夫曼树对字符进行编码,俗称哈夫曼编码。具体来讲,是将所要编码的字符作为叶子结点,该字符在文件中的使用频率作为叶子结点的权值,以自底向上的方式、通过执行n-1次的“合并”运算后构造出最终所要求的树,即哈夫曼树,它的核心思想是让权值大的叶子离根最近。 采取的贪心策略:每次从树的集合中取出双亲为0且权值最小的两棵树作为左、右子树,构造一棵新树,新树根结点的权值为其左右孩子结点权之和,将新树插入到树的集合中。

算法的设计 步骤1:确定合适的数据结构。 步骤2:初始化。构造n棵结点为n个字符的单结点树集合F={T1,T2,…, Tn},每棵树中只有一个带权的根结点,权值为该字符的使用频率; 步骤3:如果F中只剩下一棵树,则哈夫曼树构造成功,转步骤6;否则,从集合F中取出双亲为0且权值最小的两棵树Ti和Tj,将它们合并成一棵新树Zk,新树以Ti为左儿子, Tj为右儿子(反之也可以)。新树Zk的根结点的权值为Ti与Tj的权值之和; 步骤4:从集合F中删去Ti、Tj,加入Zk; 步骤5:重复步骤3和4; 步骤6:从叶子结点到根结点逆向求出每个字符的哈夫曼编码(约定左分支表示字符“0”,右分支表示字符“1”)。则从根结点到叶子结点路径上的分支字符组成的字符串即为叶子字符的哈夫曼编码。算法结束。

构造实例 【例2-3】已知某系统在通信联络中只可能出现8种字符,分别为a,b,c,d,e,f,g,h,其使用频率分别为0.05,0.29,0.07,0.08,0.14,0.23,0.03,0.11,试设计哈夫曼编码。 设权w=(5,29,7,8,14,23,3,11),n=8,按哈夫曼算法的设计步骤构造一棵哈夫曼编码树,具体过程如下:

实例构造 (1) (4) (2) (3)

(5) (6) (7)

(8)

算法描述及分析 采用线性结构实现的算法,其复杂性为O(n2) 。 算法的改进:采用极小堆实现,其复杂性为O(nlogn)。

最小生成树 设G=(V,E)是无向连通带权图。E中每条边(v,w)的权为c[v][w]。 生成树:如果G的子图G’是一棵包含G的所有顶点的树,则称G’为G的生成树。 耗费:生成树上各边权的总和 最小生成树:在G的所有生成树中,耗费最小的生成树 最小生成树在实际中有广泛应用。例如,在设计通信网络时,用图的顶点表示城市,用边(v,w)的权c[v][w]表示建立城市v和城市w之间的通信线路所需的费用,则最小生成树就给出了建立通信网络的最经济的方案。

Prim算法 算法思想 设G=(V,E)是无向连通带权图,V={1,2,…,n};设最小生成树T=(U,TE),算法结束时U=V,TE E。 首先,令U={u0},TE={}。然后,只要U是V的真子集,就做如下贪心选择:选取满足条件i U,j V-U,且边(i,j)是连接U和V-U的所有边中的最短边,即该边的权值最小。然后,将顶点j加入集合U,边(i,j)加入集合TE。继续上面的贪心选择一直进行到U=V为止,此时,选取到的所有边恰好构成G的一棵最小生成树T。需要注意的是,贪心选择这一步骤在算法中应执行多次,每执行一次,集合TE和U都将发生变化,即分别增加一条边和一个顶点。

算法设计的关键 实现Prim算法的关键 找到连接U和V-U的所有边中的最短边!为此必须知道V-U中的每一个顶点与它所连接的U中的每一个顶点的边的信息。 该信息可通过设置两个数组closest和lowcost来体现。其中,closest[j]表示V-U中的顶点j在集合U中的最近邻接顶点,lowcost[j]表示V-U中的顶点j到U中的所有顶点的最短边的权值,即边(j,closest[j])的权值。

算法设计步骤 步骤1:确定合适的数据结构。设置带权邻接矩阵C,bool数组s[],如果s[i]=true,说明顶点i已加入集合U;设置两个数组closest[]和lowcost[]; 步骤2:初始化。令集合U={u0},TE={},并初始化数组closest、lowcost和s; 步骤3:在集合V-U中寻找使得lowcost具有最小值的顶点t,t就是集合V-U中连接集合U中的所有顶点中最近的邻接顶点; 步骤4:将顶点t加入集合U,边(t,closest[t])加入集合TE; 步骤5:如果集合V=U,算法结束,否则,转步骤6; 步骤6:对集合V-U中的所有顶点k,更新其lowcost和closest,用下面的公式更新:if (C[t][k]<lowcost[k]) {lowcost[k]=C[t][k];closest[k]=t;},转步骤3。

构造实例 按Prim算法对如图2-12所示的无向连通带权图构造一棵最小生成树。 图2-12 无向连通带权图

构造过程中辅助变量值的变化 假定初始顶点为顶点1,即U={1};t:集合V-U中距离集合U最近的顶点。 U V-U 顶点编号 t 2 3 4 5 6 closest lowcost {1} {2,3,4,5,6} 1 {1,3} {2,4,5,6} - {1,3,6} {2,4,5} {1,3,4,6} {2,5} {1,2,3,4,6} {5} {1,2,3,4,5,6} {}

构造过程图示法 (1) (2) (3) (6) (5) (4)

算法分析 从算法Prim的描述可以看出,if((!s[j])&&(lowcost[j]<temp))是算法的基本语句,该语句的执行次数为n2,由此可得Prim算法的时间复杂度为O(n2)。显然该复杂性与图中的边数无关,因此,Prim算法适合于求边稠密的网的最小生成树。 容易看出,Prim算法和Dijkstra算法的用法非常相似,它们都是从余下的顶点集合中选择下一个顶点来构建一棵扩展树,但是千万不要把它们混淆了。由于解决的问题不同,计算的方式也有所不同,Dijkstra算法比较路径的长度,因此必须把边的权值相加,而Prim算法则直接比较给定的边的权值。

Kruskal算法 算法思想 设G=(V,E)是无向连通带权图,V={1,2,…,n};设最小生成树T=(V,TE),该树的初始状态为只有n个顶点而无边的非连通图T=(V,{}),Kruskal算法将这n个顶点看成是n个孤立的连通分支。它首先将所有的边按权从小到大排序。然后,只要T中的连通分支数目不为1,就做如下的贪心选择:在边集E中选取权值最小的边(i,j),如果将边(i,j)加入集合TE中不产生回路(或环),则将边(i,j)加入边集TE中,即用边(i,j)将这两个连通分支合并连接成一个连通分支;否则继续选择下一条最短边。在这两种情况下,都把边(i,j)从集合E中删去。继续上面的贪心选择直到T中所有顶点都在同一个连通分支上为止。此时,选取到的n-1条边恰好构成G的一棵最小生成树T。

算法设计的关键——避开环路 那么,怎样判断加入某条边后图T会不会出现回路呢?该算法对于手工计算十分方便,因为用肉眼可以很容易看到挑选哪些边能够避免构成回路,但使用计算机程序来实现时,还需要一种机制来进行判断。Kruskal算法用了一个非常聪明的方法,就是运用集合的性质进行判断:如果所选择加入的边的起点和终点都在T的集合里,那么就可以断定一定会形成回路。因为,根据集合的性质,如果两个点在一个集合里,就是包含的关系,那么反映到图上就一定是包含关系。

算法设计——求解步骤 步骤1:初始化。将图G的边集E中的所有边按权从小到大排序,边集TE={ },把每个顶点都初始化为一个孤立的分支,即一个顶点对应一个集合; 步骤2:在E中寻找权值最小的边(i,j); 步骤3:如果顶点i和j位于两个不同连通分支,则将边(i,j)加入边集TE,并执合并操作将两个连通分支进行合并; 步骤4:将边(i,j)从集合E中删去,即E=E-{(i,j)}; 步骤5:如果连通分支数目不为1,转步骤2;否则,算法结束,生成最小生成树T。

构造实例——图2-12所示 首先,将图的边集E中的所有边按权从小到大排序为:1(1,3)、2(4,6)、3(2,5)、4(3,6)、5(1,4) 和(2,3)和(3,4)、6(1,2)和(3,5)和(5,6)。 (1) (2) (3) (4) (5)

算法描述及分析 从算法的基本思想和设计步骤可以看出,要实现Kruskal算法必须解决以下两个关键问题:(1)选取权值最小的边的同时,要判断加入该条边后树中是否出现回路;(2)不同的连通分支如何进行合并。 为了便于解决这两大问题,必须了解每条边的信息。那么,在Kruskal算法中所设计的每条边的结点信息存储结构如下: struct edge{ double weight; //边的权值 int u,v; // u为边的起点,v为边的终点。 }bian[]; sort函数耗时最大,为此算法的时间复杂性为O(eloge)。

Prim和Kruskal算法的比较 (1)从算法的思想可以看出,如果图G中的边数较小时,可以采用Kruskal,因为Kruskal算法每次查找最短的边;边数较多可以用Prim算法,因为它是每次加一个顶点。可见,Kruskal适用于稀疏图,而Prim适用于稠密图。 (2)从时间上讲,Prim算法的时间复杂度为O(n2),Kruskal算法的时间复杂度为O(eloge)。 (3)从空间上讲,显然在Prim算法中,只需要很小的空间就可以完成算法,因为每一次都是从个别点开始出发进行扫描的,而且每一次扫描也只扫描与当前顶点集对应的边。但在Kruskal算法中,因为时刻都得知道当前边集中权值最小的边在哪里,这就需要对所有的边进行排序,对于很大的图而言,Kruskal算法需要占用比Prim算法大得多的空间。

讨论与练习 1.在正整数 n 中删除m个数字, 使得余下的数字按原次序组成的新数最大, 2. 独木舟上的旅行 题目描述: 进行一次独木舟的旅行活动,独木舟可以在港口租到,并且之间没有区别。一条独木舟最多只能乘坐两个人,且乘客的总重量不能超过独木舟的最大承载量。我们要尽量减少这次活动中的花销,所以要找出可以安置所有旅客的最少的独木舟条数。

3.有N堆果子,要将所有的果子合并为一堆,每次合并时所耗的体力为两堆果子的总重量之和,问如何移动才能使耗费的体力最少。 4.过桥问题 在漆黑的夜里,N位旅行者来到了一座狭窄而且没有护栏的桥边。如果不借助手电筒的话,大家是无论如何也不敢过桥去的。不幸的是,N个人一共只带了一只手电筒,而桥窄得只够让两个人同时过。如果各自单独过桥的话,N人所需要的时间已知;而如果两人同时过桥,所需要的时间就是走得比较慢的那个人单独行动时所需的时间。问题是,如何设计一个方案,让这N人尽快过桥。