信息学 图论最短路径.docx

上传人:b****5 文档编号:11848165 上传时间:2023-04-06 格式:DOCX 页数:19 大小:469.19KB
下载 相关 举报
信息学 图论最短路径.docx_第1页
第1页 / 共19页
信息学 图论最短路径.docx_第2页
第2页 / 共19页
信息学 图论最短路径.docx_第3页
第3页 / 共19页
信息学 图论最短路径.docx_第4页
第4页 / 共19页
信息学 图论最短路径.docx_第5页
第5页 / 共19页
点击查看更多>>
下载资源
资源描述

信息学 图论最短路径.docx

《信息学 图论最短路径.docx》由会员分享,可在线阅读,更多相关《信息学 图论最短路径.docx(19页珍藏版)》请在冰豆网上搜索。

信息学 图论最短路径.docx

信息学图论最短路径

图的最短路径

教学专题

图的最短路径

授课学时

1学时(约50分钟)

教学课型

新授课

授课对象

冬令营A层次

教学内容

教学目标

知识点

学习要求

了解

理解

掌握

最短路径问题的描述

无权图最短路径

单源最短路径(边权值非负)

Dijkstra算法中贪心策略

Dijkstra算法优化

任意顶点对间最短路径

Floyd算法中动态规划思想

图的传递闭包

教学重点

有权图最短路径

教学难点

Dijkstra算法设计过程(用flash动画模拟突破难点)

教学流程

知识点

时间分配

最短路径问题的引入

2分钟

无权图最短路径

5分钟

单源最短路径(边权值非负)

15分钟

交互讨论:

边权值为负时Dijkstra的局限性及改进

5分钟

任意顶点对间最短路径

15分钟

图的传递闭包

5分钟

媒体使用

多媒体课件:

动画模拟最短路径求解过程

教学内容及示例

设计意图

目前,互联网上有许多网站可用来查找两个地理之间的通路,例如,Google地图,Baidu地图等,这些路径查找系统都是用抽象表示国道或省市间的公路,图中的节点表示城市,图中边表示公路路段,边权值表示城市之间的距离,当然也可以表示从某地出发到达目标的时间。

例如,从苏州到南京的司机最关心的是:

(1)苏州、南京之间存在通路吗?

(2)如果苏州、南京之间有一条以上通路,哪条路最短?

目前,第一个问题我们有解决的办法,采用深度优先搜索或者广度优先搜索遍历图可以达到目的,本节课我们还将讨论其他算法。

第二个问题是本节重点讨论的,虽然我们可以枚举任一条可能路径,然后检查是否为最短,但是几乎需要n!

的时间,效率太低,我们需要更有效的。

一、无权图最短路径

例1:

如下图,假设C1,C2,C3,C4,C5,C6是六座城市,他们之间的连线表示两城市间有道路相通,如果在每一个城市均需要换乘一次飞机,问从C1到其余各城市最少换乘次数。

图1六个城市地图

对于图G=(V,E),顶点集合V和边集合E,边没有赋权值,或者也可以认为权值均为1,Ci,j=1,一条路径C1,C2……上的边数叫做无权路径长度(Unweightedpathlength)找出从C1出发到其余各点的最短路径。

因为边是没有赋权的,所以只对边的数目感兴趣,如果需要记录实际路径我们可以增加一个变量path来代表路径就可以了。

此时此刻可以说从C1到C1(它本身)的最短路径是长为0的路径,把这个信息做个标记,得到下图:

图2C1被访问

然后开始寻找所有与C1相连的顶点,它们与C1的距离为1,此时我们可以看到C2、C3与C1仅有“一边之遥”,把它们表示在下图,

图3C1被访问后对邻居顶点的影响

以此推断,到算法结束,分别如下图所示:

图4最短路径图

显然这种搜索方式就是大家学过的图的广(宽)度优先搜索(Breadth-firstsearch),该方法按层次处理顶点:

距开始点最近的顶点首先被访问到,而最远的点最后被访问。

伪代码如下:

用邻接矩阵存储图

//无权最短路径

//输入:

图的邻接矩阵ga,源点i

//输出:

源点i至图其它各点的最短路径path(需逆向追溯路径)和长度dist

procedureshortest_unweighted(i:

integer);

begin

dist[i]:

=0;

其它dist[]为一个非常大的值代表不可达;

path[i]:

=0;//0代表是起点或者访问不到;path记录由哪一点访问它

顶点i进入队列Q;

while队列Q非空do

begin

从队列Q中取出队首元素v;

known[v]:

=true;

forj:

=1tondo//依次访问v的所有可直达顶点

begin

if(notknown[j])and(ga[v,j]=1)then

begin

ifdist[j]为非常大的值thendist[j]:

=dist[v]+1;

path[j]:

=v;

顶点j进入队列Q;

end;//endofthen

end;//endoffor

end;//endofwhile

end;//endofshortest_unweithted

对于每个顶点,我们追踪了三个信息:

是否被访问过?

距起点的距离是多少,由哪个顶点去访问它

下面的一组表格显示追踪过程:

注意最开始dist的值均为非常大的值

表1追踪过程

鉴于两个对顶点集合的循环,该算法的的时间复杂度为O(|V|2)效率较低,这是因为在内部循环中,尽管所有顶点早就known了,但是循环还要继续下去。

请大家思考优化方法(提示:

使用邻接表存储)。

看能否优化到O(|V|+|E|),对于边信息较少的图有很大的速度提升。

如果图是赋权图,那么问题就明显地变得困难了,这是因为每个点的重要性不仅仅局限于离起点的远近,而跟它提供的边的权值的有关。

不过我们仍然可以使用来自无权情形时的想法。

请看下面的讨论

二、单源最短路径(从一个顶点到其余各顶点的最短路径,边的权值为非负)

例2:

如下图,假设C1,C2,C3,C4,C5,C6是六座城市,他们之间的连线表示两城市间有道路相通,连线旁的数字表示路程。

请编写一程序,找出C1到Ci的最短路径(2≤i≤6),输出路径序列及最短路径的路程长度。

图5赋权六城市地图

[问题分析]

下面给出解决这个问题的Dijkstra算法思想

Dijkstra算法像无权最短路径算法一样,按阶段进行。

在每个阶段,Dijkstra算法选择一个顶点Cm,顶点Cm须满足它在所有未访问顶点中具有最小的dist[m],同时算法声明从Ci到Cm的最短路径是已知的。

阶段的其余部分由其它顶点j的dist值的更新工作组成。

区别在于:

在无权的情形中,若dist[j]=非常大的值(如maxint),则dist[j]=dist[m]+1,而赋权的情形,如果当dist[j]的新值dist[m]+GA[m,j]是个更好的改进值,我们就让dist[j]=dist[m]+GA[m,j],即dist[j]=Min{dist[j],dist[m]+GA[m,j]},这是一次松弛操作。

简而言之,在通向j路径时,是否使用顶点m由算法来判断。

当然原始的dist[j]是没有用到dist[m]的值的。

Dijkstra算法瞬间执行图:

下一步选择顶点x

 

图6通过u到达未访问顶点序列中的x路径最短

具体算法思路如下:

设图G用邻接矩阵的方式存储在GA中,GA[i,j]=maxint表示Ci,Cj是不关联的,否则为权值(非负值)。

设集合S用来保存已求得最短路径的终点序号,初始时S=[Ci]表示只有源点,以后每求出一个终点Cj,就把它加入到集合中并作为新考虑的中间顶点。

设数组dist[1..n]用来存储当前求得的最短路径,初始时Ci,Cj如果是关联的,则dist[j]等于权值,否则等于maxint,以后随着新考虑的中间顶点越来越多,dist[j]可能越来越小。

再设一个与dist对应的数组path[1..n]用来存放当前最短路径的边,初始时为Ci到Cj的边,如果不存在边则为空。

执行时,先从S以外的顶点(即待求出最短路径的终点)所对应的dist数组元素中,找出其值最小的元素(假设为dist[m]),该元素值就是从源点Ci到终点Cm的最短路径长度,对应的path[m]中的顶点或边的序列即为最短路径。

接着把Cm并入集合S中,然后以Cm作为新考虑的中间顶点,对S以外的每个顶点Cj,比较dist[m]+GA[m,j]的dist[j]的大小,若前者小,表明加入了新的中间顶点后可以得到更好的方案,即可求得更短的路径,则用它代替dist[j],同时把Cj或边(Cm,Cj)并入到path[j]中。

从某一个顶点Ci到其余任一顶点Cj的最短路径,可能是它们之间的边(Ci,Cj),也可能是经过k个中间顶点即k+1条边所形成的路径(1≤k≤n-2)。

重复以上过程n-2次,即可在dist数组中得到从源点到其余各终点的最段路径长度,对应的path数组中保存着相应的最段路径。

对于上图,采用Dijkstra算法找出C1到Cj之间的最短路径(2≤j≤6)的过程如下:

初始时:

表2初始选择C1

1

2

3

4

5

6

S

1

0

0

0

0

0

Dist

0

4

8

maxint

maxint

maxint

Path

C1

C1,C2

C1,C3

第一次:

选择m=2,则S=[C1,C2],计算比较dist[2]+GA[2,j]与dist[j]的大小表3选择C2

1

2

3

4

5

6

S

1

1

0

0

0

0

Dist

0

4

7

8

10

maxint

Path

C1

C1,C2

C1,C2,C3

C1,C2,C4

C1,C2,C5

第二次:

选择m=3,则S=[C1,C2,C3],计算比较dist[3]+GA[3,j]与dist[j]的大小表4选择C3

1

2

3

4

5

6

S

1

1

1

0

0

0

Dist

0

4

7

8

9

maxint

Path

C1

C1,C2

C1,C2,C3

C1,C2,C4

C1,C2,C3,C5

第三次:

选择m=4,S=[C1,C2,C3,C4],计算比较dist[4]+GA[4,j]与dist[j]的大小表5选择C4

1

2

3

4

5

6

S

1

1

1

1

0

0

Dist

0

4

7

8

9

17

Path

C1

C1,C2

C1,C2,C3

C1,C2,C4

C1,C2,C3,C5

C1,C2,C4,C6

第四次:

选择m=5,则S=[C1,C2,C3,C4,C5],计算比较dist[5]+GA[5,j]与dist[j]的大小表6选择C5

1

2

3

4

5

6

S

1

1

1

1

1

0

Dist

0

4

7

8

9

13

Path

C1

C1,C2

C1,C2,C3

C1,C2,C4

C1,C2,C3,C5

C1,C2,C3,C5,C6

因为该图的度n=6,所以执行n-2=4次后结束,最后一个顶点C6如果dist不是maxint,代表有路径,它是不需要探索的。

表7C6为最后顶点

1

2

3

4

5

6

S

1

1

1

1

1

1

Dist

0

4

7

8

9

13

Path

C1

C1,C2

C1,C2,C3

C1,C2,C4

C1,C2,C3,C5

C1,C2,C3,C5,C6

此时通过dist和path数组可以看出:

C1到C2的最短路径为:

C1——C2,长度为:

4;

C1到C3的最短路径为:

C1——C2——C3,长度为:

7;

C1到C4的最短路径为:

C1——C2——C4,长度为:

8;

C1到C5的最短路径为:

C1——C2——C3——C5,长度为:

9;

C1到C6的最短路径为:

C1——C2——C3——C5——C6,长度为:

13;

下面给出具体的Dijkstra算法框架(注:

为了实现上的方便,我们用一个一维数组s[1..n]代替集合S,用来保存已求得最短路径的终点集合,即如果s[j]=0表示顶点Vj不在集合中,反之,s[j]=1表示顶点Vj已在集合中)。

主要代码如下:

//无向有权图单源最短路径

//输入:

图的邻接矩阵ga,源点i

//输出:

源点i至图其它各点的最短路径path和长度dist

ProcedureDijkstra(GA,dist,path,i);{表示求Vi到图G中其余顶点的最短路径,GA为图G的邻接矩阵,dist和path为变量型参数,其中path的基类型为集合}

Begin

Forj:

=1TonDoBegin{初始化}

Ifj<>iThens[j]:

=0

Elses[j]:

=1;

dist[j]:

=GA[i,j];

Ifdist[j]

Thenpath[j]:

=[i]+[j]

Elsepath[j]:

=[];

End;

Fork:

=1Ton-2Do

Begin

w:

=maxint;m:

=i;

Forj:

=1TonDo{求出第k个终点Cm}

If(s[j]=0)and(dist[j]

=j;w:

=dist[j];End;

Ifm<>iThens[m]:

=1elseexit;{若条件成立,则把Cm加入到S中,否则退出循环,因为剩余的终点,其最短路径长度均为maxint,无需再计算下去}

Forj:

=1TonDo{对s[j]=0的更优元素作必要修改}

If(s[j]=0)and(dist[m]+GA[m,j]

ThenBegin

Dist[j]:

=dist[m]+GA[m,j];

path[j]:

=path[m]+[j];

End;

End;

End;

请注意,图的邻接矩阵存储中,GA[I,j]存放边的值,如果边不在图中出现,我们赋予GA[I,j]一个数值很大的数。

这个大数可任选,但需要注意:

1)该数值应大于代价矩阵的最大值

2)该数值不应该dist[m]+GA[m,j]的语句溢出。

我们来分析一下Dijkstra算法的时间复杂度,对于有n个顶点的图,其复杂度为O(n2)当然如果我们选择更好的数据结构如用邻接表存储图结构,在选择m结点检查dist[m]时采用优先队列那么时间复杂度可以在O(nlogn)时间内,同学们如果感兴趣可以去查阅相关书籍。

另外我们要指出的Dijkstra算法,是20世纪中期,EdsgerDijkstra提出的一个非常简单、有效的贪心算法来求解单源最短路径问题。

它实际上是一个用于图遍历的广度优先搜索算法的一个“拓展”版本,它每次都选择了一个“领先的”结点。

它的出现可能源于下面的生活常识:

假设G的边构成一个充满水的管道系统,这个管道在结点处相交,每条边有一定的长度,现在假设在结点S处搅动一下水,并且从S开始引起一个波动。

当这个波动以常数速度向外传播时,波面扩张按照它们到S的距离增加的顺序到达结点。

一个事实:

波面到达任意结点V所走的路径恰好是一条最短路径。

对于有向无环图(DAG)同样可以采用上述算法来求单源最短路径,但是如果边权出现负值将无法使用Dijkstra算法,请看下例?

使用Dijkstra算法,得到1-->3的最短路径为1,而实际上是0。

一个诱人的方案是将一个常数加到们每一条边的权值上,如此除去负的边,再计算新图的最短路径问题,然后把结果减去常数后用到原来的图上。

这种方案不可能直接实现,因为路径有可能发生变化或者,那些有许多条边的路径变得比那些具有很少条边的路径权值和更重了。

如下图

 

图7有负权边对Dijkstra算法影响

问题出在哪里?

Dijkstra算法中顶点3先于2被声明为已经访问过的定点,1到3的路径不会发生变化,但此时恰好存在了一条可以从未访问的顶点2回到3的负权边,这就有可能导致路径1-->2-->3比1-->3更短,尽管路径1-->2比1-->3长。

这表明了一条路径可以从“昂贵”的边开始,然后接着用“负费用”的边进行补偿,Dijkstra风格的贪心方法在这里不起作用。

下面我们来看一种既可以解决边权为负,且可以同时求得任一对顶点间最短路径的算法。

三、Floyd算法

例3:

以例2图为例,求任意一对顶点之间的最短路径。

[问题分析]

这个问题的解法有两种:

一是分别以图中的每个顶点为源点共调用n次Dijkstra算法,这种算法的时间复杂度为O(n3);另外还有一种算法:

Floyd算法,它的思路简单,但时间复杂度仍然为O(n3),下面介绍Floyd算法。

带权图G的邻接矩阵用GA表示,再设一个与GA同类型的表示每对顶点之间最短路径长度的二维数组D,D的初值等于GA。

这是因为,如果不允许使用任何中间顶点,Ci到Cj的距离就是GA[I,j]。

令Dk[I,j]表示从Ci到Cj的最短路径,路径中的中间顶点编号不大于k。

特别地D的初值记为D0[I,j]。

算法从矩阵D0开始,随后一步一步计算D1,D2,D3,…,Dn。

如果已经得到了Dk-1,接下来计算Dk的方法需要考虑一下两种可能情况:

1)对任意一对顶点Ci、Cj,从Ci到Cj且中间顶点编号不大于k的最短路径,如果中间没有顶点编号为k的顶点,那么路径长度就是Dk-1[I,j](当然有可能是无路径的)。

2)如果上述那条从Ci到Cj的最短路径中包括顶点编号为k的顶点,那么路径一定是一条从Ci到Ck的最短路径再接着一条从Ck到Cj的最短路径,而且这两条子路径中都不含顶点编号大于k-1的顶点,因而它们的长度分别是Dk-1[I,k],Dk-1[k,j]。

公式:

Dk[I,j]=Min{Dk-1[I,j],Dk-1[I,k]+Dk-1[k,j]},1≤k≤n

边界条件D0[I,j]=GA[I,j],(不经过任何一个顶点时路径长度)

这是动态规划技术典型的应用之一。

Floyd算法需要在D上进行n次运算,每次以Ck(1≤k≤n)作为新考虑的中间点,求出每对顶点之间的当前最短路径长度,依次运算后,D中的每个元素D[i,j]就是图G中从顶点Ci到顶点Cj的最短路径长度。

再设一个二维数组P[1..n,1..n],记录最短路径,其元素类型为集合类型(注意有向图中此数组中只记录了包含顶点)。

Floyd算法的具体描述如下:

//无向有权图任一顶点对间最短路径

//输入:

图的邻接矩阵ga,源点i

//输出:

任意顶点间的最短路径path和长度dist

ProcedureFloyd(GA,D,P);

Begin

Fori:

=1TonDo{初始化}

Forj:

=1TonDo

Begin

D[i,j]:

=GA[i,j];

IfD[i,j]

=[i]+[j]

Elsep[i,j]:

=[];

End;

Fork:

=1TonDo{枚举n个顶点}

Fori:

=1TonDo

Forj:

=1TonDo

Begin

If(i=k)or(j=k)or(i=j)ThenContinue;

{重复点无需计算,直接进入下一轮循环}

IfD[i,k]+D[k,j]

Begin{更新最短路径长度,并保存}

D[i,j]:

=D[i,k]+D[k,j];

P[i,j]:

=P[i,k]+P[k,j];

End;

End;

End;

运用Floyd算法,找出任意一对顶点之间的最短路径及其长度。

过程如下

表格8Floyd算法执行过程

表格9Dijkstra算法和Floyd算法比较

算法名称

解决问题

算法

思想

时间

复杂度

编码

复杂度

使用限制

优化

Dijkstra

单源最短路径

贪心

O(n2)

复杂

边权非负

优先队列

斐波那契堆

Floyd

任一点对最短路径

动态

规划

O(n3)

简单

边权可为负

(无负环)

但有时,我们可能只是需要了解在图G中是否存在顶点i到顶点j的路径,这个问题称为图的传递闭包问题。

四、传递闭包

图的传递闭包主要用于计算图的连通性和图中满足条件的连通分支。

在一张图中,如何判别任两个顶点之间是否有路,我们介绍一个跟Floyd算法同样思想的Warshall算法。

其思想是通过一系列n阶布尔矩阵来构造一个给定的n阶顶点图的传递闭包。

R0,R1,…,Rk-1,Rk,…,Rn

这一系列矩阵从R0开始,R0不允许它的路径中包含任何中间顶点,所以R0就是图的邻接矩阵。

Rk表示中间顶点编号不大于k的矩阵。

如何从矩阵Rk-1的元素生成矩阵Rk的元素,有下面的公式:

Rk[i,j]=Rk-1[i,j]orRk-1[i,j]andRk-1[i,j]

这就表明:

如果一个元素Rk-1[i,j]是1,那么Rk[i,j]也是1

如果一个元素Rk-1[i,j]是0,当且仅当矩阵中第i行第k列的元素和第k行第j列的元素都是1,该元素在Rk[i,j]才会变成1

用下面的图8所示:

图8传递闭包矩阵图

算法实现时只需要把Floyd算法中路径的加法计算,改为逻辑运算即可。

五、推荐实例:

1)求图9中带权有向图中每一对顶点间最短路径。

(两种方法)

拓展1:

把V2到V0的边权值改为-3,求最短路径

拓展2:

在拓展1的基础上,将V1到V2的边权值改为-2,能否求出最短路径(提出负环问题)

图9带权有向图

2)上海世界博览会志愿者

问题描述:

虽然上海在世界博览会国家展馆之间修筑了天桥,但来到这里的游客还是喜欢走上海世博会的街道,他们总是想知道某两个展馆之间的最短距离,于是志愿者专门回答游客的这个问题,但是由于他们也不可能把这些信息完全记下来,所以总是要向总部询问。

你能胜任这个工作吗?

输入:

第一行两个数n,m(1≤n≤100,1≤m≤500)表示一共有n个体育馆,有m条询问。

接下来一个n*n的矩阵,其中第i行第j列的元素表示展馆间的距离,如果为0,表示没有直接的通路。

接下来m行,每行两个数,表示询问的两个展馆i,j

输出:

对于每条问讯输出一行,表示展馆间最短距离。

分析:

求最短距离的基础题,可以对于每条问询用Dijkstra算法求一次单源点的最短路径,也可以先用Floyd算法求出任意两点间的最短路径长度,然后对每条问询直接查找答案。

3)Car的旅行路线(NOIP2001)

4)USACO3.2.6SweetButter

六、总结与思考

最短路径依赖于一种性质:

也就是两顶点间的最短路径包含路径上其它的最短路径。

这种最优子结构性质是动态规划和贪心算法是否适用的一个标志。

最短路径问题有着很广泛的应用,通过赋权图来建模,会随着赋权的含义不同:

如费用、距离、时间、工程进度、设备租金、线路容量等来求得实际问题的最优解。

当然求解最短路径不止本节介绍几种算法,这些算法也不是最优的,还需要大家在理解的基础上,广泛采用高级的数据结构和先进的算法设计技术来进行改进和应用。

不同的最短路径问题到底用哪种算法,以及还需要对该种算法作什么改动,是非常重要的,这种能力往往需要同学们在后续的学习中总结提高。

在学习完最短路径后,我们是否联想到:

能不能修改这些算法,实现求最长路径的问题呢?

或者我们需要求第二最短路径,以便于做应急预案等等?

这种发散性的思维是值得称赞的,也希望大家在学习中以本节课为基础进一步探索。

从生活实例引入

设问展示本课

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

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

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

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