实验9最短路径问题.docx
《实验9最短路径问题.docx》由会员分享,可在线阅读,更多相关《实验9最短路径问题.docx(15页珍藏版)》请在冰豆网上搜索。
实验9最短路径问题
最短路径问题及解法
一、实验目的
1.了解求解最短路径的Dijkstra算法和Floyd算法的基本原理;
2.掌握Dijkstra算法和Floyd算法的基本步骤;
3.能应用相关的算法求解实际问题
4.掌握Dijkstra算法和Floyd算法的C程序设计技巧;
2、实验内容
1.求解最短路径的Dijkstra算法及C程序实现
2.求解最短路径的Floyd算法及C程序实现
1.
求解最短路径问题的Dijkstra算法
问题1:
已知如图1所示的单行线交通网,它是一个有向图,每条弧旁的数字表示这条单行线的长度.现在某人要从
出发,通过这个交通网到
去,求使下图中总路程最小的路径.
图1
引入基本变量:
ωij:
各边的权数,
d(v1,vj):
v1到vj的路的总权数的最小值
P标号:
从v1到vj的最短路径的权数已确定
T标号:
从v1到vj的最短路径的权数的上界
Si:
算法进行到第i步时,具P标号点的集合.
λ(v)=m:
从vs到v的最短路上,v的前一个点是vm,λ(v)=0,则v=vs.
λ(v)=M:
D中不含从vs到v的路;
1.1结合图表演示Dijkstra算法的基本步骤和基本原理
我们用图表结全的方法讲述Dijkstra算法的基本步骤
第一步
因ωij≥0,故d(v1,v1)=0.v1加上P标号(图2中用划圈表示).
图2
表1第一步
被圈去的次序
顶点
从v1到该点的最短路的长度
该节点的前一个节点
1
v1
0
起点
v2
v3
v4
v5
v6
v7
v8
v9
第二步:
从v1共发出3条弧,(v1,v2),(v1,v3),(v1,v4).
若从v1出发沿(v1,v2)到达v2,权数为d(v1,v1)+ω12=6;
若从v1出发沿(v1,v3)到达v3,权数为d(v1,v1)+ω13=3;
若从v1出发沿(v1,v4)到达v4,权数为d(v1,v1)+ω14=1.
min{d(v1,,v1)+ω12,d(v1,v1)+ω13,d(v1,v1)+ω14}
=min{6,3,1}
=d(v1,v1)+ω14=1
故从v1出发到v4的路径的最小权必为1,最短路是(v1,v4),
d(v1,v4)=1,
给v4加上P标号(图3中加圈表示).为什么?
图3
表2第二步
加P标号的次序
顶点
从v1到该点的最短路的长度
该节点的前一个节点
1
v1
0
起点
v2
6
v1
v3
3
v1
2
v4
1
v1
v5
v6
v7
v8
v9
第三步:
加上红圈的点表示具有P标号,现考查从v1及v4指向其余点的弧
从v1出发,分别沿(v1,v2)、(v1,v3)到达v2,v3,权数分另别为6和3,
从v4出发,沿(v4,v6)到达v6,权数为d(v1,v4)+ω46=1+10=11,
min{d(v1,v1)+ω12,d(v1,v1)+ω13,d(v1,v4)+ω46}
=min{6,3,11}
=d(v1,v1)+ω13=3
故从v1到v3的最短路是(v1,v3),
d(v1,v3)=3,
v3加上P标号.
图4
表3第3步
加P标号的次序
顶点
从v1到该点的最短路的长度
该节点的前一个节点
1
v1
0
起点
v2
6
v1
3
v3
3
v1
2
v4
1
v1
v5
v6
11
v4
v7
v8
v9
第四步:
加上红圈的点表示具有P标号,现考查从v1,v3和v4发出的弧.
从v1出发,沿(v1,v2)到达v2总权数为d(v1,v1)+ω12=6,
从v3出发,沿(v3,v2)到达v2总权数为d(v1,v3)+ω32=5,
5<6,因此到达v2的路径长度的上界应改为5,v2的前一个节点也应改为v3.
从v3出发,沿(v3,v4)到达v4,因v4已具有P标号,不再参与计算.
从v4出发,沿(v4,v6)到达v6权数为d(v1,v4)+ω46=1+10=11,
min{d(v1,v1)+ω12,d(v1,v3)+ω32,d(v1,v4)+ω46}
=min{6,5,11}
=d(v1,v1)+ω13=3
故从v1到v2的最短路是(v1,v3,v2),
d(v1,v2)=5,
v2加上P标号.
图5
表4第4步
加P标号的次序
顶点
从v1到该点的最短路的长度
该节点的前一个节点
1
v1
0
起点
4
v2
6,5
v1 , v3
3
v3
3
v1
2
v4
1
v1
v5
v6
11
v4
v7
v8
v9
按照同样的方法,重复这个过程即可得到从v1出发到v8的最短路,最终的表格如下:
表5最终表格
加P标号的次序
顶点
从v1到该点的最短路的长度
该节点的前一个节点
1
v1
0
起点
4
v2
6,5
v1 , v3
3
v3
3
v1
2
v4
1
v1
5
v5
6
v2
7
v6
11,10
v4 , v5
6
v7
9
v5
8
v8
12
v5
v9
从表中我们可以到得从从v1出发到v8的最短路的长度为12,具体路短为:
v8←v5←v2←v3←v1.
1.2Dijkstra算法的算法描述
上文介绍的这个算法即为Dijkstra(迪杰斯特拉)算法,它是典型的单源最短路径算法,可用于计算一个节点到其他所有节点的最短路径.主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止.对于给定的赋权有向图D=(V,A)(或无向图D=(V,E)),欲求从vs出发到vt的最短路径(链),Dijkstra方法的具体步骤如下:
Step0 :
令i=0, S0={vs},P(vs)=0,λ(vs)=0,对每一个v≠vs,令T(v)=+∞,λ(v)=M,令k=s.
Step1 :
如果vt
Si,算法终止,对每个v∈Si,d(vs,v)=P(v);否则转下一步.
Step2 :
考查每个使(vk,vj)∈A且vk
Si的点vj.
如果T(vj)>P(vk)+wkj,则把T(vj)修改为P(vk)+wkj,把λ(vj)修改为k;
否则转下一步.
Step3:
如果
,则把
的T标号变为P标号,
令
若目标终点出现在Si+1中, 终止,这时
(I)对每一个v∈Si,d(vs,v)=P(v),
(II)对每一个
,d(vs,v)=T(v).
否则令i= i+1,转入Step1.
1.3Dijkstra算法的C程序实现
我们编写了Dijkstra算法的C语言程序,并将程序应用于求解问题1,程序见附件,程序运行结果如下:
/*Dijkstra算法,键盘输入起点和终点,以图1.1的问题为例*/
#include
#defineMIN(x,y)(x<=y?
x:
y)
voidmain()
{
doublea[10][10],v[10][3],t;
inti,j,k,n=9,m,i0,m0;
for(i=0;i<=n;i++)
for(j=0;j<=n;j++)
a[i][j]=-1;
for(i=0;i<=9;i++)
for(j=0;j<=2;j++)
v[i][j]=-1;
//邻接矩阵
a[1][2]=6;a[1][3]=3;
a[1][4]=1;a[2][5]=1;
a[3][2]=2;a[3][4]=2;
a[4][6]=10;a[5][4]=6;
a[5][6]=4;a[5][7]=3;
a[5][8]=6;a[6][5]=10;
a[6][7]=2;a[7][8]=4;
a[9][8]=3;a[9][5]=2;
//i0:
起点下标,m0:
终点下标;
puts("Pleaseinputtheindexofthestaringpoint:
");
scanf("%d",&i0);
puts("Pleaseinputtheindexofterminalpoint:
");
scanf("%d",&m0);
i=i0;
m=m0;
n=9;//点的总数
v[i][0]=0;//起点到起点的距离设为0
v[i][1]=1;//划圈的点置值1,其余为-1
v[i][2]=0;//前一个点的编号
while
(1)
{
for(j=1;j<=n;j++)
{
if(v[j][1]>0)continue;
t=a[i][j];
if(t<0.0)continue;
t+=v[i][0];
if(v[j][0]<0)
{
v[j][0]=t;
v[j][2]=i;
}
elseif(t{
v[j][0]=t;
v[j][2]=i;
}
}
t=1.0e20;
for(j=1;j<=n;j++)
{
if(v[j][1]>0||v[j][0]<0)continue;
if(v[j][0]{
t=v[j][0];
k=j;
}
}
v[k][1]=1.0;
i=k;
if(k==m)break;
}
for(j=1;j<=n;j++)
printf("%3d--%5.2f--%5.2f--%5.2f\n",j,v[j][0],v[j][1],v[j][2]);
printf("Theleastchainis:
%d",m);
while
(1)
{
k=(int)(v[m0][2]);
printf("<--%d",k);
m0=k;
if(k==i0)break;
}
puts("");
}
练习:
(1)编程实现用Dijkstra方法求下面的无向图中从v1到其它某个节点的距离的最小值,要求其它某节点待定,其序号由键盘输入。
图6
(2)思考如何设计一个有类似于高德地图等手导航功能的程序。
2.求解最短路径的Floyd算法
1.2Floyd算法的基本思想
Floyd-Warshall算法(Floyd-Warshallalgorithm)是解决网络图中任意两点间的最短路径的一种算法,可以正确处理有向图或负权的最短路径问题,同时也被用于计算有向图的传递闭包. Floyd算法是一个经典的动态规划算法.
从任意节点A到任意节点B的最短路径不外乎2种可能:
1)直接从A到B,
2)从A经过若干个节点X到B.
我们假设dis(A,B)为节点A到节点B的最短路径的长度,对于每一个节点X,我们检查
dis(A,X)+dis(X,B)是否成立,如果成立,证明从A到X再到B的路径比从A直接到B的路径更短,我们便设置
dis(A,B)=dis(A,X)+dis(X,B).
这样一来,当我们遍历完所有节点X,Dis(A,B)中记录的便是A到B的最短路径的距离.
2.2Floyd算法的基本步骤
Step1用数组dis[i][j]来记录
之间的最短距离;
Step2初始化dis[i][j];
若i=j则dis[i][j]=0,
若
之间有边连接则dis[i][j]的值为该边的权值,
否则dis[i][j]的值为无穷大.
Step3对k从1到n循环,不断修正任意两点之间的最短距离.若
dis[i][k]+dis[k][j]则令dis[i][j]=dis[i][k]+dis[k][j],否则dis[i][j]的值不变.
2.3Floyd算法的正确性的证明
对于任意两点A,B:
(1)当从A到B之间的最短路径,在中间没有经过节点或经过1个节点号为1的节点时,算法显然正确.
(2)假设A到B经过的最大节点号不超过k-1时,算法得到的最短距离是正确的
(3)当A到B的路径上经过的最大节点下标为k时,则从A到节点
之间的节点号均不大于k-1,从
到B之间所有的节点号也不大于k-1,由假设2得,A到
的距离是中间节点号不超过
的最短距离,
到B的距离是中间节点号不超过k-1的最短距离,所以A经
到B为A,B之间经节点最大编号为k的路径中距离的最小值,由算法修正A,B的最短距离,即可得到A,B间节点编号不超过k的距离的最小值.
(4)综上所述,算法是正确的
2.4Floyd算法的C程序实现
我们编写了Floyd算法的C语言程序求解问题1,程序见附件,程序运行结果如下:
//Floydmethod,以图1.1中的问题为例
#include
#defineMIN(x,y)(x<=y?
x:
y)
voidmain()
{
doublea[10][10],t;
inti,j,k,n,m,i0,m0,path[10][10];
n=9;
for(i=0;i<=n;i++)
for(j=0;j<=n;j++)
a[i][j]=1.0e10;
for(i=0;i<=n;i++)
for(j=0;j<=9;j++)
path[i][j]=j;
for(i=0;i<=n;i++)
a[i][i]=0;
a[1][2]=6;a[1][3]=3;
a[1][4]=1;a[2][5]=1;
a[3][2]=2;a[3][4]=2;
a[4][6]=10;a[5][4]=6;
a[5][6]=4;a[5][7]=3;
a[5][8]=6;a[6][5]=10;
a[6][7]=2;a[7][8]=4;
a[9][8]=3;a[9][5]=2;
for(k=1;k<=n;k++)
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
if(a[i][j]>a[i][k]+a[k][j])
{
a[i][j]=a[i][k]+a[k][j];
path[i][j]=path[i][k];
}
puts("Thematrixa[][]:
");
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
printf("%5.1f",a[i][j]);
puts("");
}
puts("Thematrixpath[][]:
");
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
printf("%3d",path[i][j]);
puts("");
}
//i0:
起点下标,m0:
终点下标;
puts("Pleaseinputtheindexofthestaringpoint:
");
scanf("%d",&i0);
puts("Pleaseinputtheindexofterminalpoint:
");
scanf("%d",&m0);
printf("Thelengthofthebestpathis:
%f\n",a[i0][m0]);
printf("Thebestpathis:
\n");
while(m0!
=i0)
{
printf("%d-->",i0);
i0=path[i0][m0];
}
printf("%d\n",m0);
}
练习:
(1)编程实现用Floyd方法求下面的无向图中从v1到其它某个节点的距离的最小值,要求其它某节点待定,其序号由键盘输入。
图6