最小生成树算法及其应用.docx

上传人:b****6 文档编号:4590589 上传时间:2022-12-07 格式:DOCX 页数:9 大小:23.16KB
下载 相关 举报
最小生成树算法及其应用.docx_第1页
第1页 / 共9页
最小生成树算法及其应用.docx_第2页
第2页 / 共9页
最小生成树算法及其应用.docx_第3页
第3页 / 共9页
最小生成树算法及其应用.docx_第4页
第4页 / 共9页
最小生成树算法及其应用.docx_第5页
第5页 / 共9页
点击查看更多>>
下载资源
资源描述

最小生成树算法及其应用.docx

《最小生成树算法及其应用.docx》由会员分享,可在线阅读,更多相关《最小生成树算法及其应用.docx(9页珍藏版)》请在冰豆网上搜索。

最小生成树算法及其应用.docx

最小生成树算法及其应用

最小生成树算法及其应用

1.根底篇

1.1定义

在电路设计中,常常需要把一些电子元件的插脚用电线连接起来。

假设每根电线连接两个插脚,把所有n个插脚连接起来,只要用n-1根电线就可以了。

在所有的连接方案中,我们通常对电线总长度最小的连接方案感兴趣。

把问题转化为图论模型就是:

一个无向连通图G=〔V,E〕,V是插脚的集合,E是插脚两两之间所有可能的连接的集合。

给每条边〔u,v〕一个权值w〔u,v〕,表示连接它们所需的电线长度。

我们的目的就是找到一个无环的边集T,连接其中所有的点且使总权值最小。

总权值

既然T是连接所有点的无环边集,它一定是一棵树。

因为这棵树是从图G中生成出来的,我们把它叫做生成树。

假设一棵生成树在所有生成树中总权值最小,我们就把它称作最小生成树。

1.2求最小生成树的一般算法

解决最小生成树问题有没有一般的方法呢?

下面我们就介绍一种贪心算法。

该算法设置了集合A,该集合一直是某最小生成树的子集。

算法执行的每一步,都要决策是否把边〔u,v〕添加到集合A中,可以添加的条件是保证A∪{〔u,v〕}仍然是最小生成树的子集。

我们称像〔u,v〕这样的边为A的平安边,或称这样的边对集合A是平安的。

求最小生成树的一般算法流程如下:

GENERIC-MST〔G,w〕

1.A←Ф

2.whileA没有形成一棵生成树

3.do找出A的一条平安边〔u,v〕

4.A←A∪{〔u,v〕}

5.returnA

一开场A为Ф,显然满足最小生成树子集的条件。

之后,每一次循环都把一条A的平安边参加A中,A仍然是最小生成树。

本节的余下部分将提出一条确认平安边的规那么〔定理1〕,下一节将详细讨论运用这一规那么寻找平安边的两个有效的算法。

图1一个图的割〔S,V-S〕

首先定义几个概念。

有向图G=〔V,E〕的割〔S,V-S〕是V的一个分划。

当一条边〔u,v〕∈E的一个端点属于S而另一端点属于V-S,我们说边〔u,v〕通过割〔S,V-S〕。

假设集合A中没有边通过割,就说割不阻碍集合A。

假设某边是通过割的具有最小权值的边,那么称该边为通过割的一条轻边。

如图1,集合S中的结点为黑色结点,集合V-S中的结点为白色结点。

连接白色和黑色结点的那些边为通过该割的边。

从边上所标的权值看,边〔d,c〕为通过该割的唯一一条轻边。

子集A包含阴影覆盖的那些边,注意,由于A中没有边通过割,所以割〔S,V-S〕不阻碍A。

确认平安边的规那么由以下定理给出。

定理1设图G〔V,E〕是一无向连通图,且在E上定义了相应的实数值加权函数w,设A是E的一个子集且包含于G的某个最小生成树中,割〔S,V-S〕是G的不阻碍A的任意割且边〔u,v〕是穿过割〔S,V-S〕的一条轻边,那么边〔u,v〕对集合A是平安的。

证明:

设T是包含A的一最小生成树。

假设T包含轻边〔u,v〕,那么T包含A∪{〔u,v〕},证明就完成了。

否那么,可设法建立另一棵包含A∪{〔u,v〕}的最小生成树T',〔u,v〕对集合A仍然是平安的。

图2最小生成树T'的建立

图2示出了最小生成树T'的建立。

S中的结点为黑色,V-S中的结点为白色,边〔u,v〕与T中从u到v的通路P构成一回路。

由于边〔u,v〕通过割〔S,V-S〕,因此在T中的通路P上至少存在一条边也通过这个割。

设〔x,y〕为满足此条件的边。

因为割不阻碍A,所以边〔x,y〕不属于A。

又因为〔x,y〕处于T中从u到v的唯一通路上,所以去掉边〔x,y〕就会把T分成两个子图。

这时参加边〔u,v〕以形成一新的生成树T'=〔T-{〔x,y〕}〕∪{〔u,v〕}。

下一步我们证明T'是一棵最小生成树。

因为〔u,v〕是通过割〔S,V-S〕的一条轻边,且边〔x,y〕也通过割,所以有w〔u,v〕≤w〔x,y〕,因此w〔T'〕=w〔T〕-w〔x,y〕+w〔u,v〕≤w〔T〕。

但T是最小生成树,因此T'必定也是最小生成树。

又因为T'包含A∪{〔u,v〕},所以〔u,v〕对A是平安的。

〔证毕〕

通过定理1可以更好地理解算法GENERIC-MST在连通图G=〔V,E〕上的执行流程。

在算法执行过程中,集合A始终是无回路的,否那么包含A的最小生成树就会出现一个环,这是不可能的。

在算法执行中的任何一时刻,图GA=〔V,A〕是一森林且GA的每一连通支均为树。

此外,对A平安的任何边〔u,v〕都连接GA中不同的连通支,这是由于A∪{〔u,v〕}必定不包含回路。

随着最小生成树的|V|-1条边相继被确定,GENERIC-MST中第2-4行的循环也随之要执行|V|-1次。

初始状态下,A=Ф,GA中有|V|棵树,每个迭代过程均将减少一棵树,当森林中只包含一棵树时,算法执行终止。

下面再来看一个由定理1得出的推论,下一节中阐述的两种算法均使用了这个推论。

推论1设G=〔V,E〕是一无向连通图,且在E上定义了相应的实数值加权函数w,设A是E的子集且包含于G的某个生成树中,C为森林GA=〔V,A〕中的连通支。

假设边〔u,v〕是连接C和GA中其它某连通支的一轻边,那么边〔u,v〕对集合A是平安的。

证明:

因为割〔C,V-C〕不阻碍A,因此〔u,v〕是该割的一条轻边。

由定理1,边〔u,v〕对集合A是平安的。

〔证毕〕

1.3常用算法

GENERIC-MST是形成最小生成树的一般算法,不过我们需要的是更加详细的算法。

这一节详细讨论两种常用的算法:

Kruskal算法和Prim算法。

它们虽然都遵循所谓的"一般"算法,但在实现上有着各自的特点。

Kruskal算法:

Kruskal算法是直接建立在上一节中给出的一般最小生成树算法的根底之上的。

该算法找出森林中连结任意两棵树的所有边中具有最小权值的边〔u,v〕作为平安边,并把它添加到正在生长的森林中。

设C1和C2表示边〔u,v〕连结的两棵树。

因为〔u,v〕必是连结C1和其它某棵树的一条轻边,所以由推论1可知〔u,v〕对C1是平安边。

Kruskal算法是一种贪心算法,因为算法每一步添加到森林中的边的权值都尽可能小。

Kruskal算法的实现可以使用并查集。

每一集合包含当前森林中某个树的结点,操作FIND-SET〔u〕返回包含u的集合中的一个代表元素,因此我们可以通过测试FIND-SET〔u〕是否等同于FIND-SET〔v〕来确定两结点u和v是否属于同一棵树,通过过程UNION来完成树与树的连结。

MST-KRUSKAL〔G,w〕

1.A←Ф

2.for每个结点v∈V[G]

3.doMAKE-SET〔v〕

4.根据权w的非递减顺序对E的边进展排序

5.for每条边〔u,v〕∈E,按权的非递减次序

6.doifFIND-SET〔u〕≠FIND-SET〔v〕

7.thenA←A∪{〔u,v〕}

8.UNION〔u,v〕

9.returnA

Kruskal算法的工作流程如图3所示。

阴影覆盖的边属于正在生成的森林A。

算法按权的大小顺序考察各边。

箭头指向算法每一步所考察到的边。

第1-3行初始化集合A为空集并建立|V|棵树,每棵树包含图的一个结点。

在第4行中根据其权值非递减次序对E的边进展排序。

在第5-8行的for循环中首先检查对每条边〔u,v〕其端点u和v是否属于同一棵树。

假设是,那么把〔u,v〕参加森林就会形成一回路,所以这时放弃边〔u,v〕;假设不是,那么两结点分属不同的树,由第7行把边参加集合A中,第8行对两颗树中的结点进展归并。

图3Kruskal算法的执行流程

Kruskal算法在图G=〔V,E〕上的运行时间取决于如何实现集合的合并。

我们可以采用处径压缩的优化方法,这是目前所知的最有效的。

初始化需占用时间O〔V〕,第4行中对边进展排序需要的运行时间为O〔ElgE〕;对别离集的森林要进展O〔E〕次操作,总共需要时间为O〔Eα〔E,V〕〕,其中α函数为Ackermann函数的反函数。

因为α〔E,V〕=O〔lgE〕。

所以Kruskal算法的全部运行时间为O〔ElgE〕。

Prim算法:

正如Kruskal算法一样。

Prim算法也是最小生成树一般算法的特例。

它的执行非常类似于寻找图的最短通路的Dijkstra算法。

Prim算法的特点是集合A中的边总是只形成单棵树。

如图4所示,阴影覆盖的边属于正在生成的树,树中的结点为黑色。

在算法的每一步,树中与树外的结点确定了图的一个割,并且通过该割的轻边被加进树中。

树从任意根结点r开场形成并逐渐生长直至该树跨越了V中的所有结点。

在每一步,连接A中某结点到V-A中某结点的轻边被参加到树中。

由推论1,该规那么总是参加对A平安的边,因此当算法终止时,A中的边就成为一棵最小生成树。

因为每次添加到树中的边都是使树的权尽可能小的边,因此上述策略是"贪心"的。

有效实现Prim算法的关键是设法较容易地选择一条新的边添加到由A的边所形成的树中,在下面的伪代码中,算法的输入是连通图G和将生成的最小生成树的根r。

在算法执行过程中,不在树中的所有结点都驻留于优先级基于key域的队列Q中,对每个结点v,key[v]是连结v到树中结点的边所具有的最小权值;按常规,假设不存在这样的边那么key[v]=∞。

域π[v]表示点v的"父亲结点"。

在算法执行中,GENERIC-MST的集合A隐含地满足:

A={〔v,π[v]〕:

v∈V-{r}-Q}

当算法终止时,优先队列Q为空,因此G的最小生成树A满足:

A={〔v,π[v]〕:

v∈V-{r}}

图4Prim算法的执行流程

MST-PRIM〔G,w,r〕

1.Q←V[G]

2.for每个u∈Q3.dokey[u]←∞

4.key[r]←05.π[r]←NIL6.whileQФ

7.dou←EXTRACT-MIN〔Q〕{返回队列Q中最小的元素}

8.for每个v∈Adj[u]

9.doifv∈Qandw〔u,v〕key[v]

10.thenπ[v]←u11.key[v]←w〔u,v〕

Prim算法的工作流程如图3所示。

第1-4行初始化优先队列Q使其包含所有结点,置每个结点的key域为∞〔除根r以外〕,r的key域被置为0。

第5行初始化π[v]的值为nil,这是由于r没有父亲结点。

在整个算法中,集合V-Q包含正在生长的树中的结点。

第7行识别出与通过割〔V-Q,Q〕的一条轻边相关联的结点u∈Q〔第一次迭代例外,根据第4行这时u=r〕。

从集合Q中去掉u后把它参加到树的结点集合V-Q中。

第8-11行对与u邻接且不在树中的每个结点v的key域和π域进展更新,这样的更新保证key[v]=w〔v,π[v]〕且〔v,π[v]〕是连结v和树中某结点的一条轻边。

Prim算法的性能取决于我们如何实现优先队列Q。

假设用二叉堆来实现Q,第1-4行的初始化部分,其运行时间为O〔V〕。

第6-11行的循环需执行|V|次,且由于每次EXTRACT-MIN操作需要O〔lgV〕的时间,所以对EXTRACT-MIN的总调用时间为O〔VlgV〕。

第8-11行的for循环总共要执行O〔E〕次,这是因为所有邻接表的长度和为2|E|。

在for循环内部,第9行对队列Q的成员条件进展测试可以在常数时间内完成,这是由于我们可以为每个结点空出1位的空间来记录该结点是否在队列Q中,并在该结点被移出队列时随时对该位进展更新。

第11行的赋值语句隐含一个对堆进展调整的操作,该操作在堆上可用O〔lgV〕的时间完成。

因此,Prim算法的整个运行时间为O〔VlgV+ElgV〕=O〔ElgV〕,和Kruskal算法的运行时间一样。

1.4例题1--Maintain〔IOI2003〕

[题目大意]

在一个初始化为空的无向图中,不断参加新边。

假设当前图连通,就求出当前图最小生成树的总权值;否那么,输出-1。

[算法分析]

根据数据规模,不可能每参加一条边都重新求一下最小生成树。

所以我们的主要问题就是如何用较短的时间在一棵最小生成树中参加一条边并得到新的最小生成树。

生成树是具有n个点n-1条边的连通图,所以假设把一条新边加进去的话,就一定会形成一个环。

然后再把环上的一条边删去〔可以是新加进去的边〕,就又形成了一棵生成树。

为了保证是最小生成树,每次删除的边都必须是环上最长的。

于是不难得到下面的算法:

1.初始化。

2.读入新边,参加图中,直到连通或读完为止。

3.假设不连通,转第7步。

4.求出当前图的最小生成树。

5.读入新边:

x,y〔端点〕,c〔权值〕。

在旧图中找到从x到y的途径,假设途径上的最长边长于c,那么把新边参加,最长边去掉。

6.未读完,转第5步;否那么转第7步。

7.完毕。

[小结]

这道题目的难度并不大,其中蕴含了最小生成树的一些根本性质,比方:

在最小生成树中参加一条新边,就会构成一个环;把这个环上的最长边去掉,又可以得到一棵新的最小生成树。

2.应用篇

和其它算法一样,要真正掌握最小生成树算法,不仅要理解算法本身的内容,还要学会如何在恰当的时候运用它。

下面我来看几个运用最小生成树解题的例子:

2.1例题2--Robot〔BalkanOI2002〕

[问题描绘]

在不久的将来,机器人会在巴尔干信息学奥林匹克竞赛中把快餐传送给参赛者。

他们将用一个简单的正方形盘子装所有的食物。

不幸的是,厨房通往参赛者休息大厅的路上将会布满多种障碍物,因此,机器人不能搬运任意大小的盘子。

你的任务是设计一个程序ROBOT.EXE,用来确定提供食物的盘子的最大尺寸。

料想中的机器人将经过的道路应在由平行的墙构成的走廊中,而且走廊只能有90拐角。

走廊从X轴正方向开场。

障碍物是柱子,由点表示,且都在走廊的两墙间。

为了使机器人可以通过那条路,盘子不能碰到柱子或墙--只有盘子的边缘能"靠"到他们。

机器人和他的盘子只能在X轴或Y轴方向平移。

假定机器人的尺寸小于盘子尺寸且机器人一直完全处于盘子下方。

输入数据:

第一行是一个整数m〔1≤m≤30〕,代表一堵墙有多少程度或竖直的段组成。

在输入文件的下面m+1行是"上部"的墙的所有折点〔包括端点〕的坐标,也就是起始点有较大的坐标y的虚线。

同样地,在下面m+1行是"下部"的墙的所有折点〔包括端点〕的坐标。

输入文件下一行包含代表障碍物个数的整数n〔0≤n≤100〕。

在文件下面n行是障碍物坐标。

所有坐标都是绝对值小于32001的整数。

输出数据:

只包含一个整数,表示满足题意的盘子最大边长。

样例:

ROBOT.IN3

010201020–25

-10–2500100

10–10

-10-102

15–1565ROBOT.OUT5

[题目大意]

求可以通过一条有障碍走廊的正方形盘子的最大尺寸。

[算法分析]

一种比较容易想到的算法:

二分+模拟〔floodfill〕。

这种方法思路虽然简单,但编程复杂度很高。

那么有没有更好的算法呢?

首先,换一个角度考虑问题。

根据题意机器人是正方形,障碍物是点。

假设把机器人的尺寸移植到障碍物上,那么机器人就成了一个点,而障碍物就是正方形了。

显然,这两个问题是等价的。

要让一个点通不过,唯一的方法就是用障碍物把走廊堵住。

这里的"堵住"就是说障碍物将在走廊的两堵墙之间形成一条通路。

于是,这道题就被转化成了图论的问题。

把障碍物和墙壁看作图中的点,两点之间边的权值可以这样求得:

定义两个点p1〔x1,y1〕和p2〔x2,y2〕的间隔为max{|x1-x2|,|y1-y2|},障碍物的间隔就是障碍点的间隔;障碍物与墙壁的间隔就是障碍点与墙壁上所有点的间隔的最小值;两堵墙之间的间隔就是走廊的最小宽度。

图5间隔

把墙壁看作起点和终点,问题就转化为:

从起点到终点的所有途径中最长边的最小值是多少?

因为当边长到达一条途径上的最长边时,这条途径就"通"了,走廊也就被堵住了。

这个问题显然可以用二分+BFS解决,时间复杂度是O〔n2log〔边的权值的范围〕〕。

不过我们有更好的算法:

先求出这个图的最小生成树,那么从起点到终点途径就是我们所要找的途径,这条途径上的最长边就是问题的答案。

这样时间复杂度就只有O〔n2〕了。

可是怎么证明呢?

用反证法。

记起点为u,终点为v,最小生成树上从u到v的途径为旧途径,旧途径上的最长边为m。

假设从u到v存在一条新途径且上面的最长边短于m。

假设新途径包含m,那么新途径上的最长边不可能短于m。

与假设不符。

假设新途径不包含m,那么新途径一定"跨"过m。

如图:

x-…-a1-a2-…ak-y是旧途径上的一段,x-b1-b2-…-bl-y是"跨"过去的一段。

假设把m去掉,最小生成树将被分割成两棵子树,显然x和y分属于不同的子树〔否那么最小生成树包含一个环〕。

因此在x-b1-b2-…-bl-y上,一定存在一条边m',它的端点分属于不同的子树。

因为最小生成树中只有m的端点分属于不同的子树,所以m'不属于最小生成树。

因此m'和m一样是连接两棵子树的边。

因为新途径的最长边短于m且m'属于新途径,所以w〔m'〕w〔m〕。

把m去掉,把m'参加,将得到一棵新的生成树且它的总权值比最小生成树的还要小。

显然不可能。

综上所述,不可能存在另一条从u到v的途径,使得它的最长边短于m。

最小生成树上从u到v的途径就是最长边最短的途径,该途径上的最长边就是问题的解。

〔证毕〕

[小结]

此题有很多解法,除了刚刚说的"二分+模拟"和"二分+BFS"之外,还可以用类似Dijikstra求最短途径的方法来做。

既然如此,为什么偏要选择这种方法呢?

不仅因为它效率高,也不仅因为它编程复杂度低,最重要的是因为它是最难想到的也是最有启发性的。

就整个题目而言,把一道看似计算几何的题转化为图论模型就已经颇费脑筋了,再用最小生成树去解决这个图论题,真是难上加难。

在考虑这道题时,因为问题是在从u到v的众多途径中选择最正确的,所以我在u和v之间画了两条途径,又标上了最长边,准备考虑如何比较它们的优劣。

结果一个环出现了,而且环上还有一条最长边,于是眼前一亮:

这不是最小生成树的根本图形吗?

然后,经过不断的修正、繁琐的证明,这个算法才逐渐成熟起来。

所以,最小生成树的应用并不是想象的那么简单,它需要敏锐的洞察力、扎实的图论根底和严谨的思维。

2.2例题3--北极通讯网络〔WaterlooUniversity2002〕

[问题描绘]

北极的某区域共有n座村庄〔1n500〕,每座村庄的坐标用一对整数〔x,y〕表示,其中0x,y10000。

为了加强联络,决定在村庄之间建立通讯网络。

通讯工具可以是无线电收发机,也可以是卫星设备。

所有的村庄都可以拥有一部无线电收发机,且所有的无线电收发机型号一样。

但卫星设备数量有限,只能给一部分村庄装备卫星设备。

不同型号的无线电收发机有一个不同的参数d,两座村庄之间的间隔假设不超过d就可以用该型号的无线电收发机直接通讯,d值越大的型号价格越贵。

拥有卫星设备的两座村庄无论相距多远都可以直接通讯。

如今有k台〔0k100〕卫星设备,请你编一个程序,计算出应该如何分配这k台卫星设备,才能使所拥有的无线电收发机的d值最小,并保证每两座村庄之间都可以直接或间接地通讯。

例如,对于下面三座村庄:

AB

CA〔10,10〕B〔10,0〕C〔30,0〕

其中≈22.36

假设没有任何卫星设备或只有1台卫星设备〔k=0或k=1〕,那么满足条件的最小的d=20,因为A和B,B和C可以用无线电直接通讯;而A和C可以用B中转实现间接通讯〔即消息从A传到B,再从B传到C〕;

假设有2台卫星设备〔k=2〕,那么可以把这两台设备分别分配给B和C,这样最小的d可取10,因为A和B之间可以用无线电直接通讯;B和C之间可以用卫星直接通讯;A和C可以用B中转实现间接通讯。

假设有3台卫星设备,那么A,B,C两两之间都可以直接用卫星通讯,最小的d可取0。

[算法分析]

当正向考虑受阻时,逆向思维可能有奇效。

此题就是这样。

知道卫星设备的数量,求最小的收发间隔,可能比较困难;假设知道间隔求数量,就很简单了。

把所有可以互相通讯的村庄连接起来,构成一个图。

卫星设备的台数就是图的连通支的个数。

问题转化为:

找到一个最小的d,使得把所有权值大于d的边去掉之后,连通支的个数小于等于k。

先看一个定理。

定理2:

假设去掉所有权值大于d的边后,最小生成树被分割成为k个连通支,图也被分割成为k个连通支。

证明:

用反证法。

假设原图被分割成k'〔k'≠k〕个连通支,显然不可能k'k,所以k'k。

因此在某一图的连通支中,最小生成树被分成了至少两部分,不妨设其为T1,T2。

因为T1和T2同属于一个连通支,所以一定存在x∈T1,y∈T2,w〔x,y〕≤d。

又因为在整个最小生成树中,所以x到y的途径中一定存在一条权值大于d的边〔u,v〕〔否那么x和y就不会分属于T1和T2了〕,w〔x,y〕≤dw〔u,v〕,所以把〔x,y〕参加,把〔u,v〕去掉,将得到一棵总权值比最小生成树还小的生成树。

这显然是不可能的。

所以,原命题成立。

〔证毕〕

有了这个定理,很容易得到一个构造算法:

最小生成树的第k长边就是问题的解。

证明:

首先,d取最小生成树中第k长的边是可行的。

假设d取第k长的边,我们将去掉最小生成树中前k-1长的边,最小生成树将被分割成为k部分。

由定理2,原图也将分割成为k部分。

〔可行性〕

其次,假设d比最小生成树中第k长的边小的话,最小生成树至少被分割成为k+1部分,原图也至少被分割成为k+1部分。

与题意不符。

〔最优性〕

综上所述,最小生成树中第k长的边是使得连通支个数≤k的最小的d,即问题的解。

〔证毕

MSN空间完美搬家到新浪博客!

特别声明:

1:

资料来源于互联网,版权归属原作者

2:

资料内容属于网络意见,与本账号立场无关

3:

如有侵权,请告知,立即删除。

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高中教育 > 英语

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1