最短路径--弗洛伊德(Floyd)算法

最短路径--弗洛伊德(Floyd)算法


       最短路径问题。即寻找图中某两个特定结点间最短的路径长度。所谓图上的路径,即从图中一个起始结点到一个终止结点途中经过的所有结点序列,路径的长度即所经过的边权和。


        最短路径问题在实际中的应用也非常广泛,例如确定某两个城市间的最短行车路线长度。要解决这类问题,我们要根据图的特点和问题的特征选择不同的算法。


       我们首先来介绍第一种计算最短路径长度的算法——Floyd算法。
Floyd算法又被称为佛洛依德算法,其算法思路如下:

 
       
用邻接矩阵保存原图,那么此时邻接矩阵中edge[i][j]的值即表示从结点 i 到结点 j ,中间不经过任何结点时距离的最小值(若它们之间有多条边,取最小权值保存至邻接矩阵;也可能为无穷,即不可达)。假设结点编号为 到 N,我们再考虑从结点 到结点 j 中间只能经过编号小于等于 的结点(也可以不经过)时最短路径长度。与原始状况相比,在中间路径上可以经过的结点增加了编号为 1 的结点。我们又知道,最短路径上的结点一定不会出现重复(不考虑存在负权值的情况)。那么,某两个结点间若由于允许经过结点 1 而出现了新的最短路径,则该路径被结点 1 分割成两部分:由i到结点 1 ,同时中间路径上不经过结点 1 的第一段路径;由结点 1j ,中间路径上同样不经过结点 1 的第二段路径,其路径总长度为edge[i][1] + edge[1][j]。要确定该路径是否比不允许经过结点 时更短,我们比较edge[i][1] + edge[1][j]edge[i][j]之间的大小关系。若前者较小,则说明中间路径经过结点 1 时比原来更短,则用该值代表由 ij 中间路径结点编号小于等于 1 的最短路径长度;否则,该路径长度将依然保持原值edge[i][j],即虽然允许经过结点 1 ,但是不经过时路径长度最短。


       考虑更一般的情况,若edge[i][j]表示从结点i到结点 j ,中间只能经过编号小于k的点时的最短路径长度,我们可以由这些值确定当中间允许经过编号小于等于 k 的结点时,它们之间的最短路径长度。同样,与原情况相比,新情况中允许出现在中间路径的结点新增了编号为 k 的结点,同理我们确定edge[i][k] + edge[k][j]的值与edge[i][j]的值,若前者较小则该值代表了新情况中从结点 i 到结点 j 的最短路径长度;否则,新情况中该路径长度依旧保持不变。


       经过这样的 n 次循环后,我们即可得到所有结点间允许经过所有结点条件下的最短路径长度,该路径长度即为我们要求的最短路径长度。即若要求得 a、b 之间的最短路径长度,其答案为ans[n][a][b]的值。
       同时我们注意到,我们在通过ans[k - 1][i][j]的各值来递推求得ans[k][i][j]的值时,所有的ans[k][i][j]值将由ans[k - 1][i][j]ans[k - 1][i][k] + ans[k - 1][k][j]的大小关系确定,但同时ans[k][i][k]ans[k][k][j]必定与ans[k - 1][i][k]ans[k - 1][k][j]的值相同,即这些值不会因为本次更新而发生改变。所以我们将如上代码片段简化成如下形式:


for (int k = 1; k <= n; k++)  
for (int i = 1; i <= n; i++)  
for (int j = 1; j <= n; j++)  
{  
    if (-1 == ans[i][k] || -1 == ans[k][j]) continue;  
    if (-1 == ans[i][j] || ans[i][k] + ans[k][j] < ans[i][j])  
        ans[i][j] = ans[i][k] + ans[k][j];  
}


 
  

  

最短路 (来自九度OJ 1447)
时间限制:1 秒 内存限制:128M

题目描述:

       在每年的校赛里,所有进入决赛的同学都会获得一件很漂亮的t-shirt。但是每当我们的工作人员把上百件的衣服从商店运回到赛场的时候,却是非常累的!所以现在他们想要寻找最短的从商店到赛场的路线,你可以帮助他们吗?

输入:

       输入包括多组数据。每组数据第一行是两个整数N、M(N<=100,M<=10000),N表示成都的大街上有几个路口,标号为1的路口是商店所在地,标号为N的路口是赛场所在地,M则表示在成都有几条路。N=M=0表示输入结束。接下来M行,每行包括3个整数A,B,C(1<=A,B<=N,1<=C<=1000),表示在路口A与路口B之间有一条路,我们的工作人员需要C分钟的时间走过这条路。输入保证至少存在1条商店到赛场的路线。当输入为两个0时,输入结束。

输出:

       对于每组输入,输出一行,表示工作人员从商店走到赛场的最短时间。

       样例输入:2 11 2 33 31 2 52 3 53 1 20 0 

       样例输出:32

#include 

int ans[101][101];  //二维数组,其初始值即为该图的邻接矩阵

int main()
{
	int n, m;
	while (scanf("%d %d", &n, &m) != EOF)
	{
		if (n == 0 && m == 0) break;

		for (int i = 1; i <= n; i++)
		{
			for (int j = 1; j <= n; j++)
				ans[i][j] = -1;  //对邻接矩阵初始化,我们用-1代表无穷
			ans[i][i] = 0;  //自己到自己的路径长度设为0
		}
		while (m--)
		{
			int a, b, c;
			scanf("%d %d %d", &a, &b, &c);
			ans[a][b] = ans[b][a] = c;  //对邻接矩阵赋值,由于是无向图,该赋值操作要进行两次
		}

		for (int k = 1; k <= n; k++)  //k从1到N循环,依次代表允许经过的中间结点编号小于等于k
		for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++)  //遍历所有ans[i][j],判断其值保持原值还是将要被更新
		{
			if (-1 == ans[i][k] || -1 == ans[k][j]) continue;  //若两值中有一个值为无穷,跳过循环,保持原值
			if (-1 == ans[i][j] || ans[i][k] + ans[k][j] < ans[i][j])
				ans[i][j] = ans[i][k] + ans[k][j];  //当由于经过k可以获得更短的最短路径时,更新该值
		}
		printf("%d\n", ans[1][n]);
	}
	return 0;
}



其时间复杂度为O(N^3),所以在大部分机试题的时间允许范围内,它要求被求解图的大小不大于200个结点,若超过该数字该算法很可能因为效率不够高而被判超时。



你可能感兴趣的:(九度OJ,最短路径,Floyd,弗洛伊德算法,九度OJ)