在图中,如果要求任意两点间的距离,则可以使用Floyd
(😉)和Dijkstra(😃)。对于比较小的数据范围(一般为顶点数),可以使用Floyd算法。本文将讲述Floyd算法的原理、实现和扩展应用。
如果有哪里写得不好请各位dalao多多指教,谢谢!
不同于Dijkstra
,Floyd
算法同样适用于带负边权的最短路问题。其原理为:
令为从顶点到的最短路径。初始时,有:
其中为图的邻接矩阵,表示顶点到的边权,表示到没有连边。
接下来,考虑枚举中间点,按的路线计算最短路,则伪代码为:
for k = 1, 2, ..., n
for x = 1, 2, ..., n
for y = 1, 2, ..., n
f[x][y] = min(f[x][y], f[x][k] + f[k][y])
此时,算法结束,最终即为从到的最短路。
注意:当给定图为无向图时,对于任意,,则,可改变计算过程(如变成f[k][x]+f[k][y]
);但若是有向图,请务必按照伪代码中的顺序编写程序!
Floyd
算法的代码可在洛谷 B3647提交。下面给出这题对应的AC代码(注意是按边输入):
#include <cstdio>
#include <cstring>
#define maxn 100
using namespace std;
int dis[maxn][maxn];
int main()
{
int n, m;
scanf("%d%d", &n, &m);
// 初始化,注意初始值不能超过INT_MAX/2(防止两个INF相加溢出)
memset(dis, 0x3f, sizeof dis);
// 每个点到自己的距离为0
for(int i=0; i<n; i++)
dis[i][i] = 0;
// 读入边
while(m--)
{
int u, v, d;
scanf("%d%d%d", &u, &v, &d);
u --, v --; // 0-index
dis[u][v] = dis[v][u] = d; // 注意两个值都要设
}
// Floyd 算法流程
for(int k=0; k<n; k++) // 中间点
for(int i=0; i<n; i++) // 起始点
for(int j=0; j<n; j++) // 终点
{
int d = dis[i][k] + dis[k][j]; // i->k->j
if(d < dis[i][j]) dis[i][j] = d; // 取最短长度
}
for(int i=0; i<n; i++, putchar('\n'))
for(int j=0; j<n; j++)
printf("%d ", dis[i][j]);
return 0;
}
下面问题来了:如何输出结果路径?其实方法很简单。与Dijkstra
类似但略有不同,设的最短路径上的某一点,则状态转移时若,则不仅要更新,还要更新。最后,递归输出结果即可。
给定一张有个点,条边的简单无向图,对于每一对(),求的最短路径上的每一点。
输入:
5 6
3 4 3
4 1 1
4 5 4
1 2 2
5 2 10
3 2 7
输出:
1->2: 1 2
1->3: 1 4 3
1->4: 1 4
1->5: 1 4 5
2->3: 2 1 4 3
2->4: 2 1 4
2->5: 2 1 4 5
3->4: 3 4
3->5: 3 4 5
4->5: 4 5
#include <cstdio>
#include <cstring>
#define maxn 100
using namespace std;
int dis[maxn][maxn], path[maxn][maxn];
void print(int x, int y) // 递归输出x->y的路径,不包含y
{
int k = path[x][y]; // x->k->y
if(k == x || k == y) // 两点相邻,直接输出
printf(" %d", x);
else
{
// 分成两部分递归
print(x, k); // x->k
print(k, y); // k->y
}
}
int main()
{
int n, m;
scanf("%d%d", &n, &m);
// 初始化
memset(dis, 0x3f, sizeof dis);
for(int i=0; i<n; i++)
dis[i][i] = 0;
// 读入边
while(m--)
{
int u, v, d;
scanf("%d%d%d", &u, &v, &d);
dis[u][v] = dis[v][u] = d; // 注意两个值都要设
path[u][v] = path[v][u] = u; // 初始化path
}
// Floyd 算法流程,为了方便输出,采用1-index
for(int k=1; k<=n; k++) // 中间点
for(int i=1; i<=n; i++) // 起始点
for(int j=1; j<=n; j++) // 终点
{
int d = dis[i][k] + dis[k][j]; // i->k->j
if(d < dis[i][j]) // 更新最短路径
dis[i][j] = d, path[i][j] = k;
}
// 依次枚举(i,j) 输出路径
for(int i=1; i<n; i++)
for(int j=i+1; j<=n; j++)
{
printf("%d->%d:", i, j);
print(i, j);
printf(" %d\n", j);
}
return 0;
}
注:由于每条路径的长度不会超过,所以总时间复杂度仍为。
总结:Floyd
算法时间复杂度为,写起来很方便。如果觉得好就请给个三连,谢谢支持!