要获得带权图中任意两点之间的最短距离,可以通过多次调用求解单源最短路径的算法来实现。但floyd算法来实现会更简单。
算法步骤:
假设图由邻接矩阵存放,通过二维数组dis[maxN][maxN]给出一张有向或者无向图,dis[i][j]代表i到j的距离,没有直接连通的路径的话就初始化为无限大,用代指无限大的极大数INF代替,但这里需要防止出现溢出的情况,否则在程序执行后将出现结果错误。
然后进行relax操作,任意三个节点i,j,k,若dis[i][k]+dis[k][j]<dis[i][j],就令dis[i][j]=dis[i][k]+dis[k][j],这样就完成了一次松弛,并且通过三层循环嵌套将最终求解多源最短路径的问题。时间复杂度为O(n^3)。
代码:
- const int maxN=1005;
- const int INF=1e9;
- #define min(a,b) (a)<(b)?(a):(b)
- int dis[maxN][maxN];
- int N;
- void floyd(){
- for(int k=1;k<=N;k++){
- for(int i=1;i<=N;i++){
- for(int j=1;j<=N;j++){
- dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
- }
- }
- }
- }
dis数组初始情况:无向图情况dis[i][j]=dis[j][i],无向图的话dis[i][j]严格代表从 i 到 j 的距离,两种情况中对于 i 和 j 中没有路径直连的情况都将dis[i][j]赋值为一个极大数INF代表无限大表示此路不通。
关于为什么仅仅只需要通过三次循环就能找到任意两个点之间的距离而不需要考虑任何次序。
原理
问题的最终解决实际上是有一个个子问题的解决而解决的,比如说某两点间的最短路径经过的节点依次是a1,a2,a3,a4,则a1~a4可以看成是a1~a2+a2~a4,最终变成是a1-a2+a2-a3+a3-a4,则这个问题的解决就变成了这四个节点的发现过程(发现理解为发现正确的最短路径即取道这些节点会比之前的路径更短并且是最短,具体体现就是dis[i][j]中的值最终确定不会再发生改变了),而a1-a2,a2-a3,a3-a4这几条边一开始就是对应节点间的最短路径,也就是说这三组节点中任意一组间的最短距离就是其初始的值在整个循环中这些值都不会再变小,否则就会与a1-a2-a3-a4为a1到a4的最短路径的这个前提条件相矛盾。
并且当k=i时,会松弛所有实际最短路径经过i的两点间最短路径,比如说当k=a2时,a1,a3将松弛为最短,由于a3与a4一开始就为最短,如果先松弛的是a1-a3再松弛a1-a4的话,a1-a4也将松弛为最短,不然的话在k=a3时a1-a4一定会被松弛为最短,不需要考虑k访问节点的顺序也就k=a2在前还是k=a3在前,结果相同,对于一条包含n个节点的路径,k每经过一个节点ai必定会将ai-1~ai+1松弛为最短,其余则会被松弛一定程度直至最短。
如果把松弛至最短这个条件看成包含N个节点的图中的一条边的话,因为N个节点只需要N-1条边就能连通,也就是说对于一条路径最多松弛N-1次就能找到最短路径,只要k经过了a1~an中的所有节点,a1-an一定会被松弛为最短也就找到了a1-an的最短路径,k会遍历完全部节点,显然满足条件。
上面的a1.....an是代指,a1并不代表编号为1的节点,不要被a1误导。
也就是说在一次最外层的循环体中所有节点间最短路径的求解是同时进行的,只是说任何两对节点的最短距离出现的时间可能不同,但当循环结束完,所有的节点间的最短路径已经求出。
完整示例(hdu-2544):
- #include<bits/stdc++.h>
- using namespace std;
- const int maxN=1005;
- const int INF=1e9;
- #define min(a,b) (a)<(b)?(a):(b)
- int dis[maxN][maxN];
- int N;
- void floyd(){
- for(int k=1;k<=N;k++){
- for(int i=1;i<=N;i++){
- for(int j=1;j<=N;j++){
- dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
- }
- }
- }
- }
- int main(){
- int m,a,b,c;
- while(cin>>N>>m&&(N+m)){
- for(int i=0;i<=N;i++){
- for(int j=0;j<=N;j++){
- dis[i][j]=INF;
- }
- dis[i][i]=0;
- }
- for(int i=1;i<=m;i++){
- cin>>a>>b>>c;
- dis[a][b]=dis[b][a]=c;
- }
- floyd();
- cout<<dis[1][N]<<endl;
- }
- }