信息学奥赛一本通 第4章第3-4节 图论算法(C++版)_第1页
信息学奥赛一本通 第4章第3-4节 图论算法(C++版)_第2页
信息学奥赛一本通 第4章第3-4节 图论算法(C++版)_第3页
信息学奥赛一本通 第4章第3-4节 图论算法(C++版)_第4页
信息学奥赛一本通 第4章第3-4节 图论算法(C++版)_第5页
已阅读5页,还剩40页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、第四章第三节 最短路径算法 如下图所示,我们把边带有权值的图称为带权图。边的权值可以理解为两点之间的距离。一张图中任意两点间会有不同的路径相连。最短路径就是指连接两点的这些路径中最短的一条。我们有四种算法可以有效地解决最短路径问题。有一点需要读者特别注意:边的权值可以为负。当出现负边权时,有些算法不适用。一、求出最短路径的长度以下没有特别说明的话,disuv表示从u到v最短路径长度,wuv表示连接u,v的边的长度。1.Floyed-Warshall算法 O(N3) 简称Floyed(弗洛伊德)算法,是最简单的最短路径算法,可以计算图中任意两点间的最短路径。Floyed的时间复杂度是O (N3)

2、,适用于出现负边权的情况。算法描述: 初始化:点u、v如果有边相连,则disuv=wuv。如果不相连则disuv=0 x7fffffffFor (k = 1; k = n; k+) For (i = 1; i = n; i+) For (j = 1; j disik + diskj) disij = disik + diskj; 算法结束:disij得出的就是从i到j的最短路径。算法分析&思想讲解:三层循环,第一层循环中间点k,第二第三层循环起点终点i、j,算法的思想很容易理解:如果点i到点k的距离加上点k到点j的距离小于原先点i到点j的距离,那么就用这个更短的路径长度来更新原先点i到点j的距

3、离。在上图中,因为dis13+dis32dis12,所以就用dis13+dis32来更新原先1到2的距离。我们在初始化时,把不相连的点之间的距离设为一个很大的数,不妨可以看作这两点相隔很远很远,如果两者之间有最短路径的话,就会更新成最短路径的长度。Floyed算法的时间复杂度是O(N3)。 1 2 3 216 Floyed算法变形: 如果是一个没有边权的图,把相连的两点间的距离设为disij=true,不相连的两点设为disij=false,用Floyed算法的变形:For (k = 1; k = n; k+) For (i = 1; i = n; i+) For (j = 1; j = n;

4、 j+) disij = disij | (disik & diskj); 用这个办法可以判断一张图中的两点是否相连。 最后再强调一点:用来循环中间点的变量k必须放在最外面一层循环。【例4-1】、最短路径问题【问题描述】平面上有n个点(n=100),每个点的坐标均在-1000010000之间。其中的一些点之间有连线。若有连线,则表示可从一个点到达另一个点,即两点间有通路,通路的距离为两点间的直线距离。现在的任务是找出从一点到另一点之间的最短路径。【输入格式】 输入文件为short.in,共n+m+3行,其中: 第一行为整数n。 第2行到第n+1行(共n行) ,每行两个整数x和y,描述了一个点的

5、坐标。 第n+2行为一个整数m,表示图中连线的个数。 此后的m 行,每行描述一条连线,由两个整数i和j组成,表示第i个点和第j个点之间有连线。 最后一行:两个整数s和t,分别表示源点和目标点。 【输出格式】 输出文件为short.out,仅一行,一个实数(保留两位小数),表示从s到t的最短路径长度。【输入样例输入样例】5 0 0 2 0 2 2 0 2 3 1 5 1 2 1 3 1 4 2 5 3 5 1 5 【输出样例输出样例】3.41 【参考程序】#include#include#include#includeusing namespace std;int a1013;double f1

6、01101;int n,i,j,k,x,y,m,s,e;int main() freopen(short.in,r,stdin); freopen(short.out,w,stdout); cin n; for (i = 1; i ai1 ai2; cin m; memset(f,0 x7f,sizeof(f); /初始化f数组为最大值 for (i = 1; i x y; fyx = fxy = sqrt(pow(double(ax1-ay1),2)+pow(double(ax2-ay2),2); /pow(x,y)表示xy,其中x,y必须为double类型,要用cmath库 cin s e

7、; for (k = 1; k = n; k+) /floyed 最短路算法 for (i = 1; i = n; i+) for (j = 1; j = n; j+) if (i != j) & (i != k) & (j != k) & (fik+fkj fij) fij = fik + fkj; printf(%.2lfn,fse); return 0;【例例4-2】牛的旅行牛的旅行【问题描述问题描述】农民农民JohnJohn的农场里有很多牧区。有的路径连接一些特定的牧区。一的农场里有很多牧区。有的路径连接一些特定的牧区。一片所有连通的牧区称为一个牧场。但是就目前而言,你能看到至少有两片

8、所有连通的牧区称为一个牧场。但是就目前而言,你能看到至少有两个牧区不连通。现在,个牧区不连通。现在,JohnJohn想在农场里添加一条路径想在农场里添加一条路径 ( ( 注意,恰好一注意,恰好一条条 ) )。对这条路径有这样的限制:一个牧场的直径就是牧场中最远的两。对这条路径有这样的限制:一个牧场的直径就是牧场中最远的两个牧区的距离个牧区的距离 ( ( 本题中所提到的所有距离指的都是最短的距离本题中所提到的所有距离指的都是最短的距离 ) )。考。考虑如下的两个牧场,图是有虑如下的两个牧场,图是有5 5个牧区的牧场,牧区用个牧区的牧场,牧区用“* *”表示,路径表示,路径用直线表示。每一个牧区都

9、有自己的坐标:用直线表示。每一个牧区都有自己的坐标: 图所示的牧场的直径大约是图所示的牧场的直径大约是12.07106, 12.07106, 最远的两个牧区是最远的两个牧区是A A和和E E,它们之间的最短路径是,它们之间的最短路径是A-B-EA-B-E。 这两个牧场都在这两个牧场都在JohnJohn的的农场上。农场上。JohnJohn将会在两个牧场中各选一个牧区,然后用一条路径连将会在两个牧场中各选一个牧区,然后用一条路径连起来,使得连通后这个新的更大的牧场有最小的直径。注意,如果起来,使得连通后这个新的更大的牧场有最小的直径。注意,如果两条路径中途相交,我们不认为它们是连通的。只有两条路径

10、在同两条路径中途相交,我们不认为它们是连通的。只有两条路径在同一个牧区相交,我们才认为它们是连通的。一个牧区相交,我们才认为它们是连通的。 现在请你编程找现在请你编程找出一条连接两个不同牧场的路径,使得连上这条路径后,这个更大出一条连接两个不同牧场的路径,使得连上这条路径后,这个更大的新牧场有最小的直径。的新牧场有最小的直径。【输入格式输入格式】 第第 1 1 行:一个整数行:一个整数N (1 = N = N (1 = N = 150), 150), 表示牧区数;表示牧区数; 第第 2 2 到到 N+1 N+1 行:每行两个整数行:每行两个整数X X,Y ( 0 = XY ( 0 = X,Y=

11、 Y= 100000 )100000 ), 表示表示N N个牧区的坐标。每个牧个牧区的坐标。每个牧区的坐标都是不一样的。区的坐标都是不一样的。 第第 N+2 N+2 行行到第到第 2 2* *N+1 N+1 行:每行包括行:每行包括N N个数字个数字 ( 0( 0或或1 ) 1 ) 表示一个对称邻接矩阵。表示一个对称邻接矩阵。 例如,例如,题目描述中的两个牧场的矩阵描述如下:题目描述中的两个牧场的矩阵描述如下: A B C D E F G H A B C D E F G H A 0 1 0 0 0 0 0 0 A 0 1 0 0 0 0 0 0 B 1 0 1 1 1 0 0 0 B 1 0

12、1 1 1 0 0 0 C 0 1 0 0 1 0 0 0 C 0 1 0 0 1 0 0 0 D 0 1 0 0 1 0 0 0 D 0 1 0 0 1 0 0 0 E 0 1 1 1 0 0 0 0 E 0 1 1 1 0 0 0 0 F 0 0 0 0 0 0 1 0 F 0 0 0 0 0 0 1 0 G 0 0 0 0 0 1 0 1 G 0 0 0 0 0 1 0 1 H 0 0 0 0 0 0 1 0 H 0 0 0 0 0 0 1 0 输入数据中至少包括两个不连通的牧区。输入数据中至少包括两个不连通的牧区。【输出格式输出格式】 只有一行,包括一个实数,表示所只有一行,包括一个实

13、数,表示所求答案。数字保留六位小数。求答案。数字保留六位小数。【输入样例输入样例】 8 8 10 1010 10 15 1015 10 20 1020 10 15 1515 15 20 1520 15 30 1530 15 25 1025 10 30 1030 10 0100000001000000 1011100010111000 0100100001001000 0100100001001000 0111000001110000 0000001000000010 0000010100000101 0000001000000010【输出样例输出样例】 22.07106822.071068 【

14、算法分析】 用Floyed求出任两点间的最短路,然后求出每个点到所有可达的点的最大距离,记做mdisi。(Floyed算法) r1=max(mdisi) 然后枚举不连通的两点i,j,把他们连通,则新的直径是mdisi+mdisj+(i,j)间的距离。 r2=min(mdisi+mdisj+disi,j) re=max(r1,r2) re就是所求。【参考程序参考程序】#include#include#include#includeusing namespace std;using namespace std;double f151151,m151,minx,r,temp,x151,y151,ma

15、xint=1e12;double f151151,m151,minx,r,temp,x151,y151,maxint=1e12;double dist(int i,int j)double dist(int i,int j) return sqrt(xi-xj) return sqrt(xi-xj)* *(xi-xj)+(yi-yj)(xi-xj)+(yi-yj)* *(yi-yj) ; (yi-yj) ; int main()int main() int i,j,n,k;char c; int i,j,n,k;char c; cinn; cinn; for(i=1;ixiyi; for(i=

16、1;ixiyi; for(i=1;i=n;i+) for(i=1;i=n;i+) for(j=1;j=n;j+) for(j=1;jc; cinc; if(c=1)fij=dist(i,j); if(c=1)fij=dist(i,j); else fij=maxint; else fij=maxint; for(k=1;k=n;k+) for(k=1;k=n;k+) for(i=1;i=n;i+) for(i=1;i=n;i+) for(j=1;j=n;j+) for(j=1;j=n;j+) if(i!=j&i!=k&j!=k) if(i!=j&i!=k&j!=k) if(fikmaxint-

17、1&fkjmaxint-1) if(fikmaxint-1&fkjfik+fkj) if(fijfik+fkj) fij=fik+fkj; fij=fik+fkj; memset(m,0,sizeof(m);memset(m,0,sizeof(m); for(i=1;i=n;i+) for(i=1;i=n;i+) for(j=1;j=n;j+) for(j=1;j=n;j+) if(fijmaxint-1&mifij)mi=fij; if(fijmaxint-1&mifij)mi=fij; minx=1e20; minx=1e20; for(i=1;i=n;i+) for(i=1;i=n;i+

18、) for(j=1;j=n;j+) for(j=1;jmaxint-1) if(i!=j&fijmaxint-1) temp=dist(i,j); temp=dist(i,j); if(minxmi+mj+temp)minx=mi+mj+temp; if(minxmi+mj+temp)minx=mi+mj+temp; r=0; r=0; for(i=1;iminx)minx=mi; for(i=1;iminx)minx=mi; printf(%.6lf,minx); printf(%.6lf,minx); return 0; return 0; 2.2.Dijkstra算法算法O (N2)用来

19、计算从一个点到其他所有点的最短路径的算法,是一种单源最短路径算法。也就用来计算从一个点到其他所有点的最短路径的算法,是一种单源最短路径算法。也就是说,只能计算起点只有一个的情况。是说,只能计算起点只有一个的情况。Dijkstraijkstra的时间复杂度是的时间复杂度是O (N2),它不能处理存在负边权的情况。,它不能处理存在负边权的情况。算法描述:算法描述: 设起点为设起点为s,disv表示从表示从s到到v的最短路径,的最短路径,prev为为v的前驱节点,用来输出路径。的前驱节点,用来输出路径。 a)a)初始化:初始化:disv=(vs); diss=0; pres=0 0; b)b)For

20、 (i = 1; i = n ; i+)(i = 1; i = n ; i+) 1.在没有被访问过的点中找一个顶点在没有被访问过的点中找一个顶点u使得使得disu是最小的。是最小的。 2.u标记为已确定最短路径标记为已确定最短路径 3.For 与与u相连的每个未确定最短路径的顶点相连的每个未确定最短路径的顶点v if ( (disu+wuv disv) ) disv = disu + wuv; prev = u; c)c)算法结束:算法结束:disv为为s到到v的最短距离;的最短距离;prev为为v的前驱节点,用来输出路径。的前驱节点,用来输出路径。算法分析算法分析&思想讲解:思想讲解: 从起

21、点到一个点的最短路径一定会经过至少一个从起点到一个点的最短路径一定会经过至少一个“中转点中转点”(例如(例如下图下图1到到5的最短路径,中转点是的最短路径,中转点是2。特殊地,我们认为起点。特殊地,我们认为起点1也是一个也是一个“中中转点转点”)。显而易见,如果我们想求出起点到一个点的最短路径,那我们)。显而易见,如果我们想求出起点到一个点的最短路径,那我们必然要先求出中转点的最短路径(例如我们必须先求出点必然要先求出中转点的最短路径(例如我们必须先求出点2 的最短路径后,的最短路径后,才能求出从起点到才能求出从起点到5的最短路径)。的最短路径)。中转点中转点终点终点最短路最短路1 11 10

22、 01 1221、233 31、2、344 41、254 换句话说,如果起点换句话说,如果起点1到某一点到某一点V0的最短路径要经过中转点的最短路径要经过中转点Vi,那么,那么中转点中转点Vi一定是先于一定是先于V0被确定了最短路径的点。被确定了最短路径的点。 我们把点分为两类,一类是已确定最短路径的点,称为我们把点分为两类,一类是已确定最短路径的点,称为“白点白点”,另一类是未确定最,另一类是未确定最短路径的点,称为短路径的点,称为“蓝点蓝点”。如果我们要求出一个点的最短路径,就是把这个点由蓝点变为。如果我们要求出一个点的最短路径,就是把这个点由蓝点变为白点。从起点到蓝点的最短路径上的中转点

23、在这个时刻只能是白点。白点。从起点到蓝点的最短路径上的中转点在这个时刻只能是白点。 Dijkstra Dijkstra的算法思想,就是一开始将起点到起点的距离标记为的算法思想,就是一开始将起点到起点的距离标记为0,而后进行,而后进行n次循环,次循环,每次找出一个到起点距离每次找出一个到起点距离disu最短的点最短的点u,将它从蓝点变为白点。随后枚举所有的蓝点,将它从蓝点变为白点。随后枚举所有的蓝点vi,如果以此白点为中转到达蓝点如果以此白点为中转到达蓝点vi的路径的路径disu+wuvi更短的话,这将它作为更短的话,这将它作为vi的的“更短路更短路径径”disvi(此时还不确定是不是(此时还不

24、确定是不是vi的最短路径)。的最短路径)。 就这样,我们每找到一个白点,就尝试着用它修改其他所有的蓝点。中转点先于终点就这样,我们每找到一个白点,就尝试着用它修改其他所有的蓝点。中转点先于终点变成白点,故每一个终点一定能够被它的最后一个中转点所修改,而求得最短路径。变成白点,故每一个终点一定能够被它的最后一个中转点所修改,而求得最短路径。123452471162求解顺序让我们对以上这段枯燥的文字做一番模拟,加深理解。让我们对以上这段枯燥的文字做一番模拟,加深理解。 算法开始时,作为起点的算法开始时,作为起点的dis1 = 0,其他的点,其他的点disi = 0 x7fffffffffffff。

25、123452471162第一轮循环找到dis1最小,将1变成白点。对所有的蓝点做出修改,使得dis2=2,dis3=4,dis4=7。这时这时dis2 2,dis3 3,dis4 4被它的最后一个中转点被它的最后一个中转点1修改为了最短路径。修改为了最短路径。123452471162第二轮循环找到dis2最小,将2变成白点。对所有的蓝点做出修改,使得dis3=3,dis5=4。这时这时dis3,dis5被它们的最后一个中转点被它们的最后一个中转点2修改为了最短路径。修改为了最短路径。123452471162第三轮循环找到dis3最小,将3变成白点。对所有的蓝点做出修改,使得dis4=4。发现以

26、3为中转不能修改5,说明3不是5的最后一个中转点。这时这时dis4也被它的最后一个中转点也被它的最后一个中转点3修改为了最短路径。修改为了最短路径。接下来的两轮循环将接下来的两轮循环将4、5也变成白点。也变成白点。N轮循环结束后,所有的点的最短路径即能求出。轮循环结束后,所有的点的最短路径即能求出。Dijkstraijkstra无法处理边权为负的情况,例如右图这个例子。无法处理边权为负的情况,例如右图这个例子。2 2到到3的边权值为的边权值为-4,显然从起点,显然从起点1到到3的最短路径是的最短路径是-2(123),但是),但是dijskstra在第二轮循环在第二轮循环开始时会找到当前开始时会

27、找到当前disi最小的点最小的点3,并标记它为白点。,并标记它为白点。这时的这时的dis3=1,然而,然而1却不是从起点到点却不是从起点到点3的最短路径。因为的最短路径。因为3已被标记为白点,最短路径值已被标记为白点,最短路径值dis3不会再被修改了,所以我们在边权存在负数的情况下得到了错误的答案!不会再被修改了,所以我们在边权存在负数的情况下得到了错误的答案!12345213-4562【例例4-3】、最短路径问题、最短路径问题(Dijkstra) 题目参见题目参见“Floyed算法算法”,但本题要求使用,但本题要求使用dijkstra算法解决。算法解决。#include#include#in

28、clude#includeusing namespace std;int a1013;double c101;bool b101;double f101101;int n,i,j,k,x,y,m,s,e;double minl;double maxx = 1e30;int main() cin n; for (i = 1; i ai1 ai2; for (i = 1; i = n; i+) for(j = 1; j m; for (i = 1; i x y; fxy = fyx = = sqrt(pow(double(ax1-ay1),2)+pow(double(ax2-ay2),2); ;

29、cin s e; for (i = 1; i = n; i+) ci = fsi; memset(b,false,sizeof(b); /dijkstra /dijkstra 最短路最短路 bs = true; cs = 0; for (i = 1; i = n-1; i+) minl = maxx; k = 0; for (j = 1; j = n; j+) / /查找可以更新的点查找可以更新的点 if (! bj) & (cj minl) minl = cj; k = j; if (k = 0) break; bk = true; for (j = 1; j = n; j+) if (ck

30、 + fkj cj) cj = ck + fkj; printf(%.2lfn,ce); return 0;【例例4-4】最小花费最小花费【问题描述问题描述】 在在n n个人中,某些人的银行账号之间可以互相转账。这些人之间转账的手续费各不相同。个人中,某些人的银行账号之间可以互相转账。这些人之间转账的手续费各不相同。给定这些人之间转账时需要从转账金额里扣除百分之几的手续费,请问给定这些人之间转账时需要从转账金额里扣除百分之几的手续费,请问A A最少需要多少钱使得最少需要多少钱使得转账后转账后B B收到收到100100元。元。【输入格式输入格式】 第一行输入两个正整数第一行输入两个正整数n,mn

31、,m,分别表示总人数和可以互相转账的人的对数。,分别表示总人数和可以互相转账的人的对数。 以下以下m m行每行输入三个正整数行每行输入三个正整数x,y,zx,y,z,表示标号为,表示标号为x x的人和标号为的人和标号为y y的人之间互相转账需要扣的人之间互相转账需要扣除除z%z%的手续费的手续费 (z100)(z100)。 最后一行输入两个正整数最后一行输入两个正整数A,BA,B。数据保证。数据保证A A与与B B之间可以直接或间接地转账。之间可以直接或间接地转账。【输出格式输出格式】 输出输出A A使得使得B B到账到账100100元最少需要的总费用。精确到小数点后元最少需要的总费用。精确到

32、小数点后8 8位。位。【输入样例输入样例】 3 3 3 3 1 2 1 1 2 1 2 3 2 2 3 2 1 3 3 1 3 3 1 3 1 3【输出样例输出样例】 103.07153164 103.07153164【数据规模数据规模】 1=n=2000 1=n=2000【参考程序参考程序】#include#includeusing namespace std;using namespace std;double a20012001,dis2001=0,minn;double a20012001,dis2001=0,minn;int n,m,i,j,k,x,y,f2001=0;int n,m

33、,i,j,k,x,y,f2001=0;void init()void init() cinnm; cinnm; for(i=1;i=m;i+) for(i=1;ixy; cinxy; void dijkstra(int x)void dijkstra(int x) for(i=1;i=n;i+)disi=axi; for(i=1;i=n;i+)disi=axi; disx=1;fx=1; disx=1;fx=1; for(i=1;i=n-1;i+) for(i=1;i=n-1;i+) minn=0; minn=0; for(j=1;j=n;j+) for(j=1;jminn)k=j;minn=

34、disj; if(fj=0&disjminn)k=j;minn=disj; fk=1; fk=1; if(k=y)break; if(k=y)break; for(j=1;j=n;j+) for(j=1;jdisj)disj=diskakjdisj)disj=disk* *akj;akj; int main()int main() init(); init(); dijkstra(x); dijkstra(x); printf(%0.8lf,100/disy); printf(%0.8lf,100/disy); return 0; return 0; 3.3.Bellman-Ford算法算法O

35、(NE)简称简称FordFord(福特)算法,同样是用来计算从一个点到其他所有点的最短路径的算(福特)算法,同样是用来计算从一个点到其他所有点的最短路径的算法,也是一种单源最短路径算法。法,也是一种单源最短路径算法。能够处理存在负边权的情况,但无法处理存在负权回路的情况(下文会有详细说能够处理存在负边权的情况,但无法处理存在负权回路的情况(下文会有详细说明)。明)。算法时间复杂度:算法时间复杂度:O(NE),N是顶点数,是顶点数,E是边数。是边数。算法实现:算法实现:设设s为起点,为起点,disv即为即为s到到v的最短距离,的最短距离,prev为为v前驱。前驱。wj是边是边j的长度,且的长度,

36、且j连连接接u、v。初始化:初始化:diss=0,disv=(vs),),pres=0For (i = 1; i = n-1; i+)(i = 1; i = n-1; i+)For (j = 1; j = E; j+)(j = 1; j = E; j+) / /注意要枚举所有边,不能枚举点。注意要枚举所有边,不能枚举点。 if ( (disu+wjdisv) ) /u /u、v分别是这条边连接的两个点。分别是这条边连接的两个点。 disv =disu + wj; prev = u; 算法分析算法分析& &思想讲解:思想讲解:Bellman-Ford算法的思想很简单。一开始认为起点是白点算法的思

37、想很简单。一开始认为起点是白点(dis1=0),每一次都枚举所有的边,必然,每一次都枚举所有的边,必然会有一些边,连接着白点和蓝点。因此每次都能用所有的白点去修改所有的蓝点,每次循环也必然会有一些边,连接着白点和蓝点。因此每次都能用所有的白点去修改所有的蓝点,每次循环也必然会有至少一个蓝点变成白点。会有至少一个蓝点变成白点。在上面这个简单的模拟中能看到白点的在上面这个简单的模拟中能看到白点的“蔓延蔓延”情况。情况。负权回路:负权回路:虽然虽然Bellman-Ford算法可以求出存在负边权情况下的最短路径,却无法解决存在负权回算法可以求出存在负边权情况下的最短路径,却无法解决存在负权回路的情况。

38、路的情况。 负权回路是指边权之和为负数的一条回路,上图中负权回路是指边权之和为负数的一条回路,上图中- - - - -这条回路的边权之和为这条回路的边权之和为-3。在有负权回路的情况下,从在有负权回路的情况下,从1到到6的最短路径是多少?答案是无穷小,因为我们可以绕这条负权回的最短路径是多少?答案是无穷小,因为我们可以绕这条负权回路走无数圈,每走一圈路径值就减去路走无数圈,每走一圈路径值就减去3,最终达到无穷小。,最终达到无穷小。所以说存在负权回路的图无法求出最短路径,所以说存在负权回路的图无法求出最短路径,Bellman-Ford算法可以在有负权回路的情况下算法可以在有负权回路的情况下输出错

39、误提示。输出错误提示。 如果在如果在Bellman-Ford算法的两重循环完成后,还是存在某条边使得:算法的两重循环完成后,还是存在某条边使得:disu+wdisv,则存在负权回路:则存在负权回路:ForFor每条边每条边(u,v) (u,v) I If f ( (disu+wdisvdisu+wdisv) ) return False return False如果我们规定每条边只能走一次,在这个前提下可以求出负权回路的最短路径。这个问题就如果我们规定每条边只能走一次,在这个前提下可以求出负权回路的最短路径。这个问题就留待读者自己思考(提示:对留待读者自己思考(提示:对Floyed做一点小处理

40、)。做一点小处理)。【例【例4-5】、最短路径问题、最短路径问题(Bellman-Ford) 题目参见题目参见“Floyed算法算法”,要求采用,要求采用Bellman-Ford算法。算法。#include#include#includeusing namespace std;int main() double a1013,dis1001,w1001,min1; int n,m,x,y,k,f10013,s,t; bool b101; cinn; for (int i=1;im; for (int i=1;i=m;i+) /初始化数组初始化数组dis,f disi=0 x7ffffffffff

41、fff/3/3; fi1 = fi2 = fi1 = fi2 = 0 x7fffffffffffff/3/3; for (int i=1;ist; diss=0; for (int i=1;i=n;i+) / /算法主体算法主体 for (int j=1;j=m;j+) if (disfj1+wjdisfj2) disfj2=disfj1+wj; if (disfj2+wjdisu+wudisvdisu+wuvv) ) disv=disu+wu disv=disu+wuv;v; prev=u; prev=u; i if f (!(!existvexistv) ) / /队列中不存在队列中不存在

42、v v点,点,v v入队。入队。 / /尾指针下移一位,尾指针下移一位,v v入队入队; ; e existv=true;xistv=true; while (head tail); while (head tail);循环队列:采用循环队列能够降低队列大小,队列长度只需开到2*n+5即可。例题中的参考程序使用了循环队列。【例例4-6】、香甜的黄油、香甜的黄油(Sweet Butter) )【问题描述问题描述】农夫农夫John发现做出全威斯康辛州最甜的黄油的方法:糖。把糖放在一片牧场上,他知道发现做出全威斯康辛州最甜的黄油的方法:糖。把糖放在一片牧场上,他知道N(1=N=500)只奶牛会过来舔

43、它,这样就能做出能卖好价钱的超)只奶牛会过来舔它,这样就能做出能卖好价钱的超甜黄油。当然,他将付出额外的费用在奶牛上。甜黄油。当然,他将付出额外的费用在奶牛上。 农夫农夫John很狡猾。像以前的巴甫洛夫,他知道他可以训练这些奶牛,让它们在听到铃声时去一个特定的牧场。他打算将糖放在那里然后下午发出铃声,以至很狡猾。像以前的巴甫洛夫,他知道他可以训练这些奶牛,让它们在听到铃声时去一个特定的牧场。他打算将糖放在那里然后下午发出铃声,以至他可以在晚上挤奶。他可以在晚上挤奶。农夫农夫John知道每只奶牛都在各自喜欢的牧场(一个牧场不一定只有一头牛)。给出各头牛在的牧场和牧场间的路线,找出使所有牛到达的路

44、程和最短的牧场知道每只奶牛都在各自喜欢的牧场(一个牧场不一定只有一头牛)。给出各头牛在的牧场和牧场间的路线,找出使所有牛到达的路程和最短的牧场(他将把糖放在那)。(他将把糖放在那)。 【输入格式输入格式】butter.in第一行第一行: 三个数:奶牛数三个数:奶牛数N,牧场数,牧场数P(2=P=800),牧场间道路数),牧场间道路数C(1=C=1450)。第二行到第第二行到第N+1行行: 1到到N头奶牛所在的牧场号。头奶牛所在的牧场号。第第N+2行到第行到第N+C+1行:每行有三个数:相连的牧场行:每行有三个数:相连的牧场A、B,两牧场间距(,两牧场间距(1=D=255),当然,连接是双向的。

45、),当然,连接是双向的。【输出格式输出格式】butter.out 一行一行 输出奶牛必须行走的最小的距离和输出奶牛必须行走的最小的距离和.【输入样例输入样例】3 4 52341 2 11 3 52 3 72 4 33 4 5样例图形样例图形 P2 P2 P1 -1- C1P1 -1- C1 | | | | 5 7 3 5 7 3 | | | C3 | C3 C2 -5- C2 -5- P3 P4 P3 P4【输出样例输出样例】8 / /说明:放在说明:放在4号牧场最优号牧场最优【参考程序参考程序】#include#include#include#include#include#includeu

46、sing namespace std;using namespace std;int n,p,c,i,j,x,y,t,min1,head,tail,tot,u;int n,p,c,i,j,x,y,t,min1,head,tail,tot,u;int a801801,b501,dis801,num801,w801801,team1601;int a801801,b501,dis801,num801,w801801,team1601;bool exist801;bool exist801;int main()int main() freopen(butter.in,r,stdin); freope

47、n(butter.in,r,stdin); freopen(butter.out,w,stdout); freopen(butter.out,w,stdout); cinnpc; cinnpc; for(i=1;i=p;i+) for(i=1;i=p;i+) bi=0;bi=0; numi=0;numi=0; for(j=1;j=p;j+) for(j=1;j=p;j+) wij= wij=0 x7fffffff/30 x7fffffff/3; ; for(i=1;i=n;i+) for(i=1;ibi; cinbi; for(i=1;i=c;i+) for(i=1;ixyt; cinxyt;

48、 wxy=t; wxy=t; ax+numx=y; ax+numx=y; ay+numy=x; ay+numy=x; wyx=wxy; wyx=wxy; min1= min1=0 x7fffffff0 x7fffffff/3/3; ; for(i=1;i=p;i+) for(i=1;i=p;i+) for(j=1;j=p;j+) disj= for(j=1;j=p;j+) disj=0 x7fffffff0 x7fffffff/3/3; ; memset(team,0,sizeof(team); memset(team,0,sizeof(team); / /队列数组初始化队列数组初始化 mem

49、set(exist,false,sizeof(exist); memset(exist,false,sizeof(exist); /exist /exist标志初始化标志初始化 disi=0;team1=i;head=0;tail=1;existi=true; disi=0;team1=i;head=0;tail=1;existi=true; / /起始点入队起始点入队 do do head+; head+; head=(head-1)%1601)+1; head=(head-1)%1601)+1; / /循环队列处理循环队列处理 u=teamhead; u=teamhead; existu=

50、false; existu=false; for(j=1;j= for(j=1;jdisu+wuauj) if (disaujdisu+wuauj) disauj=disu+wuauj; disauj=disu+wuauj; if (!existauj) if (!existauj) tail+; tail+; tail=(tail-1)%1601)+1; tail=(tail-1)%1601)+1; teamtail=auj; teamtail=auj; existauj=true; existauj=true; while(head!=tail); while(head!=tail); t

51、ot=0; tot=0; for(j=1;j=n;j+) for(j=1;j=n;j+) t totot+ +=disbj;=disbj; if (totmin1) min1=tot; if (totmin1) min1=tot; coutmin1; coutmin1; return 0; return 0; 二、输出最短路径二、输出最短路径1.1.单源最短路径的输出单源最短路径的输出Dijkstraijkstra,Bellman-Ford,SPFA都是单源最短路径算法,它们的共同点是都都是单源最短路径算法,它们的共同点是都有一个数组有一个数组prex 用来记录从起点到用来记录从起点到x的最短

52、路径中,的最短路径中,x的前驱结点是哪个。的前驱结点是哪个。每次更新,我们就给每次更新,我们就给prex 赋一个新值,结合上面的思想讲解,相信对于记录赋一个新值,结合上面的思想讲解,相信对于记录某点的前驱结点是不难理解的。那么怎么利用某点的前驱结点是不难理解的。那么怎么利用prex 数组输出最短路径方案呢?数组输出最短路径方案呢?【例【例4-7】、最短路径问题】、最短路径问题(输出路径输出路径)要求改写程序,用要求改写程序,用Dijkstra、Bellman-Ford、SPFA算法输出最短路径的方案。算法输出最短路径的方案。使用一个小小的递归过程就能解决这一问题。使用一个小小的递归过程就能解决

53、这一问题。void print(int x) if (pre if (preax = 0) return; /x = 0) return; /起点的前驱我们已设为起点的前驱我们已设为0print(preax); cout x; / /主程序中:主程序中:mainmain (进行(进行Dijkstra或或Bellman-Ford,SPFA运算)运算)cout sout s; print print(e); /s /s是起点,是起点,e是终点是终点 2.2.Floyed算法输出最短路径算法输出最短路径FloyedFloyed算法输出路径也是采用记录前驱点的方式。因为算法输出路径也是采用记录前驱点的

54、方式。因为floyed是计算任意两点是计算任意两点间最短路径的算法,间最短路径的算法,disij记录从记录从i到到j的最短路径值。故而我们定义的最短路径值。故而我们定义preij为一个二维数组,记录从为一个二维数组,记录从i到到j的最短路径中,的最短路径中,j的前驱点是哪一个。的前驱点是哪一个。【例例4-8】、最短路径问题、最短路径问题(Floyed法输出路径法输出路径)要求改写要求改写Floyed的程序,模仿的程序,模仿Dijkstra输出路径的方法用输出路径的方法用floyed输出最短路径方案。输出最短路径方案。【参考程序参考程序】#include#include#includeusing

55、 namespace std;double dis101101;int x101,y101;int pre101101;int n,i,j,k,m,a,b;int pf(int x) return x*x;void print(int x) if (preax = 0) return; /prea if (preax = 0) return; /preaa=0,a=0,说明已经递归到起点说明已经递归到起点a print(preax); cout n; cin n; for (i = 1; i = n; i+) for (i = 1; i xi yi; cin xi yi; memset(dis

56、,0 x7f,sizeof(dis); memset(dis,0 x7f,sizeof(dis); /初始化数组初始化数组 cin m; cin m; memset(pre,0,sizeof(pre); memset(pre,0,sizeof(pre); / /初始化前驱数组初始化前驱数组 for (i = 1; i = m; i+) for (i = 1; i a b; cin a b; disab = disba = sqrt(pf(xa-xb)+pf(ya-yb); disab = disba = sqrt(pf(xa-xb)+pf(ya-yb); preab = a; preab =

57、a; /a /a与与b b相连,自然从相连,自然从a a到到b b的最短路径的最短路径b b的前驱是的前驱是a a preba = b; preba = b; cin a b; cin a b; for (k = 1; k = n; k+) for (k = 1; k = n; k+) /floyed /floyed 最短路最短路 for (i = 1; i = n; i+) for (i = 1; i = n; i+) for (j = 1; j = n; j+) for (j = 1; j disik+diskj) if (disij disik+diskj) disij = disik

58、+ diskj; disij = disik + diskj; preij = prekj; preij = prekj; / /从从i i到到j j的最短路径更新为的最短路径更新为i ik kj j,那么,那么i i到到j j最短路径最短路径j j的前驱就肯定与的前驱就肯定与k k到到j j最短路径最短路径j j的前驱相同。的前驱相同。 cout a; cout a; print(b); /a print(b); /a是起点,是起点,b b是终点是终点 return 0; return 0; 最后再稍微提一提有向图求最短路径的方法:对有向图求最短路径上面的算法可以直接使用,只需注意如最后再稍

59、微提一提有向图求最短路径的方法:对有向图求最短路径上面的算法可以直接使用,只需注意如果从果从i到到j只有一条有向边,只有一条有向边,wij赋值为这条边的权值,而将赋值为这条边的权值,而将wji赋值为无穷大即可。赋值为无穷大即可。【上机练习】1、信使【问题描述】 战争时期,前线有n个哨所,每个哨所可能会与其他若干个哨所之间有通信联系。信使负责在哨所之间传递信息,当然,这是要花费一定时间的(以天为单位)。指挥部设在第一个哨所。当指挥部下达一个命令后,指挥部就派出若干个信使向与指挥部相连的哨所送信。当一个哨所接到信后,这个哨所内的信使们也以同样的方式向其他哨所送信。直至所有n个哨所全部接到命令后,送

60、信才算成功。因为准备充足,每个哨所内都安排了足够的信使(如果一个哨所与其他k个哨所有通信联系的话,这个哨所内至少会配备k个信使)。 现在总指挥请你编一个程序,计算出完成整个送信过程最短需要多少时间。【输入格式】 输入文件msner.in,第1行有两个整数n和m,中间用1个空格隔开,分别表示有n个哨所和m条通信线路。1=n=100。 第2至m+1行:每行三个整数i、j、k,中间用1个空格隔开,表示第i个和第j个哨所之间存在通信线路,且这条线路要花费k天。【输出格式】 输出文件msner.out,仅一个整数,表示完成整个送信过程的最短时间。如果不是所有的哨所都能收到信,就输出-1。 【输入样例】

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论