第一节 Floyd-Warshall算法
本算法可以求任意两个点之间的最短路径,又称“多源最短路径”,其时间复杂度为O(n^3)
其核心部分只有下面几行,注意加法的溢出处理
//floyd最短路径算法的核心部分 for(int k = 0; k < n; ++ k){ for(int i = 0 ; i < n ; ++ i){ for(int j = 0 ; j < n ; ++ j){ if(grid[i][k]!=INT_MAX && grid[k][j]!=INT_MAX &&grid[i][j] > grid[i][k]+grid[k][j]){ grid[i][j] = grid[i][k] + grid[k][j]; path[i][j] = k; } } } }
/* *基于有向图的floyd最短路径算法 *floyd算法不能解决带有“负权回路”的图 */ #include <iostream> #include <vector> #include <algorithm> #include <climits> #include <cstdio> using namespace std; typedef vector<vector<int> > VVI; void floyd(VVI& grid, VVI& path){ int n = grid.size(); //floyd最短路径算法的核心部分 for(int k = 0; k < n; ++ k){ for(int i = 0 ; i < n ; ++ i){ for(int j = 0 ; j < n ; ++ j){ if(grid[i][k]!=INT_MAX && grid[k][j]!=INT_MAX &&grid[i][j] > grid[i][k]+grid[k][j]){ grid[i][j] = grid[i][k] + grid[k][j]; path[i][j] = k; } } } } } void print(int x, int y, VVI& path){ int k = path[x][y]; //cout<<"k="<<k<<endl; if(k==-1) return; print(x,k,path); printf("%d->",k); print(k,y,path); } int main(){ int n,m; cin >> n >> m; VVI grid(n,vector<int>(m,INT_MAX)); for(int i = 0 ; i < n; ++ i) grid[i][i] = 0; for(int i = 0 ; i < m; ++ i){ int a,b,e; cin >> a >> b >> e; grid[--a][--b] = e; } VVI path(n,vector<int>(m,-1)); floyd(grid,path); //输出最终的结果 cout<<string(30,'=')<<endl; printf("start end length path "); for(int i = 0 ; i < n ; ++ i){ for(int j = 0 ; j < n; ++ j){ printf("%4d %4d %4d ",i+1,j+1,grid[i][j]); printf("%d->",i+1); print(i,j,path); printf("%d ",j+1); } //printf(" "); } cout<<string(30,'=')<<endl; return 0; }
第二节 Dijkstra算法-通过边实现松弛
/* *单源最短路径算法 */ #include <iostream> #include <vector> #include <algorithm> #include <climits> using namespace std; typedef vector<vector<int> > VVI; typedef vector<bool> VB; typedef vector<int> VI; void printPath(VI& path, int u, int v){ vector<int> res; res.push_back(v+1); while(path[v]!=u){ res.push_back(path[v]+1); v = path[v]; } res.push_back(u+1); reverse(res.begin(),res.end()); cout<<"路径:"; for(int i = 0 ; i < res.size(); ++ i) cout << "->"<< res[i]; cout<<endl; } int main(){ int n,m; cin >> n >>m; VVI grid(n,vector<int>(n,INT_MAX)); for(int i = 0 ; i < n ; ++ i) grid[i][i] = 0; for(int i = 0 ; i < m; ++ i){ int a,b,e; cin >> a >> b >> e; --a;--b; grid[a][b] = e; } VB visit(n,false); visit[0] = true; VI dist(grid[0].begin(),grid[0].end()); VI path(n,0); for(int i = 0 ; i < n ; ++ i) { if(dist[i] == INT_MAX) path[i] = -1; else path[i] = 0; } //Dijkstra算法核心语句 for(int i = 0 ; i < n-1; ++ i){ int minDist = INT_MAX, minIndex = i; for(int j = 0 ; j < n ; ++ j){ if(!visit[j] && dist[j] < minDist){ minDist = dist[j]; minIndex = j; } } visit[minIndex] = true; for(int p = 0; p < n;++ p){ if(grid[minIndex][p] < INT_MAX ){ if(dist[p] > dist[minIndex]+grid[minIndex][p]){ dist[p] = dist[minIndex] + grid[minIndex][p]; path[p] = minIndex; } } } } printPath(path,0,n-1); //输出最终的结果 cout<<"距离:"; for(int i = 0 ; i < n; ++ i) cout<<"->"<<dist[i]; cout<<endl; return 0; }
第三节 Bellman-Ford-解决负权边
Dijkstra算法不能解决带有负权边的图,而Bellman-Ford能计算
注意松弛操作只需要进行n-1轮即可,因为在一个含有n个顶点的图中,任意两点之间的最短路径最多包含n-1边
#include <iostream> #include <vector> #include <algorithm> #define INF 100000 using namespace std; struct edge{ int u; int v; int w; edge(int uu = 0, int vv = 0 , int ww = 0):u(uu),v(vv),w(ww){} }; int main(){ int n,m; cin >> n >> m; vector<edge> e(m); for(int i = 0 ; i < m; ++ i){ cin >>e[i].u >> e[i].v >> e[i].w; --e[i].u;--e[i].v; } vector<int> dist(n,INF),bak(n,INF); dist[0] = 0; for(int i = 0; i <n-1; ++ i){ for(int j = 0; j < n; ++ j) bak[j] = dist[j]; for(int j = 0; j < m; ++ j){ if(dist[e[j].v] > dist[e[j].u] + e[j].w){ dist[e[j].v] = dist[e[j].u] + e[j].w; } } //检测数组是否有更新,如果没有更新,提前退出循环 bool check = false; for(int j = 0; j < n ; ++ j) if(bak[j]!=dist[j]){check=true;break;} if(!check) break; } //检测负权回路 bool flag = false; for(int i = 0 ; i < m ; ++ i){ if(dist[e[i].v] > dist[e[i].u] + e[i].w){ flag = true; break; } } if(flag) cout<<"此图含有负权回路"<<endl; for(int i = 0 ; i < n; ++ i){ cout<<dist[i]<<" "; } cout<<endl; }