最短路径
Date:2019-06-18 14:14:31
单源最短路径
- 即求图中某一顶点到其他各顶点的最短路径
Dijskra算法
- 权值非负
伪码描述
1 //G为图,全局变量;数组d为源点到达各个点的最短路径长度;s为初始顶点 2 Dijskra (G, d[], s) 3 { 4 初始化; 5 for(i<=n) 6 { 7 u = 使d[u]最小的还未被访问的定点的编号 8 vis[u] = 1; 9 for(从u出发能到达的所有顶点v) 10 { 11 if(vis[v]==0 && 以u为中介点使得s到v的距离比d[v]更近) 12 优化d[v]; 13 令v的前驱pre[v]=u来记录最短路径 14 } 15 } 16 }
邻接矩阵实现
1 const int M = 1e3, INF=1x3fffffff; 2 int n, grap[M][M], vis[M]={0}, d[M]; 3 4 void Dijskra(int s) //s为起点 5 { 6 fill(d, d+M, INF); //数组整体赋值 7 fill(vis, vis+M, 0); 8 d[s] = 0; //s到自身为0 9 for(int i=1; i<=n; i++) 10 { 11 int u=0, Min=INF; 12 for(int j=1; j<=n; j++) //寻找未访问的结点中最小的结点u 13 if(vis[j]==0 && d[j]<Min) 14 { //第一次执行该循环后,u = s; 15 u = j; 16 Min = d[j]; 17 } 18 if(u == 0) return ; //图不连通 19 vis[u] = 1; 20 for(int v=1; v<=n; v++) 21 { 22 if(vis[v]==0 && grap[u][v]!=INF && d[u]+grap[u][v]<d[v]) 23 d[v] = d[u] + grap[u][v]; 24 } 25 } 26 }
邻接表实现
1 struct node 2 { 3 int v, w; 4 }; 5 6 vector<node> adj[M]; 7 int d[M], vis[M], n; 8 9 void Dijskra(int s) 10 { 11 fill(d, d+M, INF); 12 fill(vis,vis+M,0); 13 d[s] = 0; 14 for(int i=1; i<=n; i++) 15 { 16 int u=0, Min=INF; 17 for(int j=1; j<=n; j++) 18 if(vis[j]==0 && d[j]<Min) 19 { 20 u = j; 21 Min = d[j]; 22 } 23 if(u == 0) return; 24 vis[u] = 1; 25 for(int j=1; j<=n; j++) 26 { 27 int v = adj[u][j].v; 28 if(vis[v]==0 && d[u]+adj[u][j].w<d[v]) 29 d[v] = d[u]+adj[u][j].w; 30 } 31 } 32 }
多条最短路径求解第二尺度最优路径
1 //Q1: 求代价最小的最短路径 2 int cost[M][M]; //cost[u][v]表示u到v的代价 3 int c[M] //c[u]表示s到u的最少代价 4 5 fill(c,c+M,INF); 6 c[s]=0; 7 for(int v=1; v<=n; v++) 8 { 9 if(vis[v]==0 && grap[u][v]!=INF) 10 { 11 if(d[u]+grap[u][v]<d[v]) 12 { 13 d[v] = d[u] + grap[u][v]; 14 c[c] = c[u] + cost[u][v]; 15 } 16 else if(d[u]+grap[u][v]==d[v] && c[u]+cost[u][v]<c[v]) 17 c[v] = c[u] + cost[u][v]; 18 } 19 } 20 21 //Q2:求点权最大的最短路径 22 int weight[M]; 23 int w[M]; 24 25 fill(w,w+M, 0); 26 w[s]=weight[s]; 27 for(int v=1; v<=n; v++) 28 { 29 if(vis[v]==0 && grap[u][v]!=INF) 30 { 31 if(d[u]+grap[u][v] < d[v]) 32 { 33 d[v] = d[u] + grap[u][v]; 34 w[v] = w[u] + weight[v]; 35 } 36 else if(d[u]+grap[u][v]==d[v] && w[u]+weight[v]>w[v]) 37 w[v] = w[u] + weight[v]; 38 } 39 } 40 41 //Q3:求最短路径的条数 42 int num[M]; 43 44 fill(num, num+M, 0); 45 num[s] = 1; 46 for(int v=1; v<=n; v++) 47 { 48 if(d[u]+grap[u][v] < d[v]) 49 { 50 d[v] = d[u] + grap[u][v]; 51 num[v] = num[u]; 52 } 53 else if(d[u]+grap[u][v]==d[v]) 54 num[v] += num[u]; 55 }
算法实现
1 /* 2 Sample Input: 3 //v, e, s 4 6 8 0 5 0 1 1 6 0 3 4 7 0 4 4 8 1 3 2 9 2 5 1 10 3 2 2 11 3 4 3 12 4 5 3 13 Sample Output: 14 0 1 5 3 4 6 15 */ 16 17 #include<cstdio> 18 #include<algorithm> 19 using namespace std; 20 const int M=1e3, INF=1e9; 21 int n,m,s,grap[M][M],vis[M],d[M],path[M]; 22 23 void Dijskra(int s) 24 { 25 fill(d, d+M, INF); 26 fill(vis,vis+M,0); 27 for(int i=0; i<n; i++) 28 path[i] = i; 29 d[s]=0; 30 for(int i=0; i<n; i++) 31 { 32 int u=-1, Min=INF; 33 for(int j=0; j<n; j++) 34 { 35 if(vis[j]==0 && d[j]<Min) 36 { 37 u = j; 38 Min = d[j]; 39 } 40 } 41 if(u==-1) return; 42 vis[u] = 1; 43 for(int v=0; v<n; v++) 44 { 45 if(vis[v]==0 && grap[u][v]!=INF && d[u]+grap[u][v]<d[v]) 46 { 47 d[v] = d[u] + grap[u][v]; 48 path[v] = u; 49 } 50 } 51 } 52 } 53 54 void DFS(int s, int v) //输出最短路径 55 { 56 if(s == v) 57 { 58 printf("%d ", s); 59 return; 60 } 61 DFS(s,path[v]); 62 printf("%d ", v); 63 } 64 65 int main() 66 { 67 #ifdef ONLINE_JUDGE 68 #else 69 freopen("Test.txt", "r", stdin); 70 #endif 71 72 scanf("%d%d%d", &n,&m,&s); 73 fill(grap[0], grap[0]+M*M, INF); 74 75 int u,v,w; 76 for(int i=0; i<m; i++) 77 { 78 scanf("%d%d%d", &u,&v,&w); 79 grap[u][v]=w; 80 grap[v][u]=w; 81 } 82 Dijskra(s); 83 //输出最短距离 84 for(int i=0; i<n; i++) 85 printf("%d ", d[i]); 86 printf("\n"); 87 //输出最短路径 88 DFS(s,5); 89 printf("\n"); 90 91 return 0; 92 }
Dijskra算法+DFS
- 含有多个标准尺度的最短路径
1 //Dijskra:记录所有的最短路径 2 vector<int> pre[M]; 3 4 void Dijskra(int s) 5 { 6 fill(d,d+M,INF); 7 d[s] = 0; 8 for(int i=0; i<n; i++) 9 { 10 int u=-1, Min=INF; 11 for(int j=0; j<n; j++) 12 if(vis[j]==0 && d[j]<Min) 13 { 14 u = j; 15 Min = d[j]; 16 } 17 if(u == -1) return; 18 vis[u] = 1; 19 for(int v=0; v<n; v++) 20 { 21 if(vis[v]==0 && grap[u][v]!=INF) 22 { 23 if(d[u]+grap[u][v] < d[v]) 24 { 25 d[v] = d[u] + grap[u][v]; 26 pre[v].clear(); 27 pre[v].push_back(u); 28 } 29 else if(d[u]+grap[u][v] == d[v]) 30 pre[v].push_back(u); 31 } 32 } 33 } 34 } 35 36 //DFS:从所有的最短路径中找出第二标尺最优的路径 37 int optValue=0,cnt=0; //最优值 38 vector<int> pre, optPath, tempPath; //path为逆序,即v->s 39 40 void DFS(int v) 41 { 42 if(v == s) //s为pre递归树的叶子结点,也即图的起点 43 { 44 tempPath.push_back(s); 45 46 int value=0; 47 //计算边权之和 48 for(int i=tempPath.size()-1; i>0; i--) 49 { 50 int id = tempPath[i]; idNext=tempPath[i-1]; 51 value += v[id][idNext]; 52 } 53 //计算点权之和 54 for(int i=tempPath.size()-1; i>=0; i--) 55 { 56 int id = tempPath[i]; 57 value += w[id]; 58 } 59 //计算路径条数 60 cnt++; 61 62 //选择最优路径 63 if(value > optValue) 64 { 65 optValue = value; 66 optPath = tempPath; 67 } 68 tempPath.pop_back(); 69 return ; 70 } 71 72 tempPath.push_back(v); 73 for(int i=0; i<pre[v].size(); i++) 74 DFS(pre[v][i]); 75 tempPath.pop_back(); 76 }
Bellman-Ford算法
- 处理带有负权值的单源最短路径
邻接表实现
1 //邻接表法 2 int n,d[M],w[M][M]; 3 vector<int> grap[M]; 4 5 //判断有无负环并且给出最短路径 6 bool Bellman(int s) 7 { 8 fill(d,d+M,INF); 9 d[s] = 0; 10 for(int i=0; i<n-1; i++) 11 { 12 for(int u=0; u<n; u++) 13 { 14 for(int j=0; j<grap[u].size(); j++) 15 { 16 int v = grap[u][j]; 17 if(d[u]+w[u][v] < d[v]) 18 d[v] = d[u] + w[u][v]; 19 } 20 } 21 } 22 23 //判断负环 24 for(int u=0; u<n; u++) 25 { 26 for(int j=0; j<grap[u].size(); j++) 27 { 28 int v = grap[u][j]; 29 if(d[u]+w[u][v] < d[v]) 30 return false; 31 } 32 } 33 return true; 34 }
算法实现
1 /* 2 Sample Input: 3 5 6 0 2 4 1 2 1 5 3 5 0 1 1 6 0 2 2 7 0 3 1 8 1 2 1 9 2 4 1 10 3 4 1 11 Sample Output: 12 2 4 13 */ 14 15 #include<cstdio> 16 #include<cstring> 17 #include<vector> 18 #include<set> 19 #include<algorithm> 20 using namespace std; 21 const int M=1e3,INF=1e9; 22 struct node 23 { 24 int v, dis; 25 node(int _v, int _dis) : v(_v), dis(_dis) {} 26 }; 27 vector<node> adj[M]; 28 int n,weight[M]; 29 int d[M],w[M],t[M]; 30 set<int> pre[M]; 31 32 void Bellman(int s) 33 { 34 fill(d, d+M, INF); 35 fill(t, t+M, 0); 36 fill(w, w+M, 0); 37 d[s] = 0; 38 w[s] = weight[s]; 39 t[s] = 1; 40 for(int i=0; i<n-1; i++) 41 { 42 bool isLoose = true; 43 for(int u=0; u<n; u++) 44 { 45 for(int j=0; j<adj[u].size(); j++) 46 { 47 int v = adj[u][j].v; 48 int dis = adj[u][j].dis; 49 if(d[u]+dis < d[v]) 50 { 51 d[v] = d[u] + dis; 52 w[v] = w[u] + weight[v]; 53 t[v] = t[u]; 54 pre[v].clear(); 55 pre[v].insert(u); 56 isLoose = false; 57 } 58 else if(d[u]+dis == d[v]) 59 { 60 if(w[u]+weight[v] > w[v]) 61 w[v] = w[u] + weight[v]; 62 pre[v].insert(u); //BF算法会多次访问同一个结点,使用set可以去重 63 t[v]=0; 64 set<int>::iterator it; 65 for(it=pre[v].begin(); it!=pre[v].end(); it++) 66 t[v] += t[*it]; //重新计算最短路径条数 67 } 68 } 69 } 70 if(isLoose) 71 break; //不再松弛时退出 72 } 73 } 74 75 int main() 76 { 77 #ifdef ONLINE 78 #else 79 freopen("Test.txt", "r", stdin); 80 #endif // ONLINE 81 82 int m,s,v; 83 scanf("%d%d%d%d", &n,&m,&s,&v); 84 for(int i=0; i<n; i++) 85 scanf("%d", &weight[i]); 86 int v1,v2,c; 87 for(int i=0; i<m; i++) 88 { 89 scanf("%d%d%d", &v1,&v2,&c); 90 adj[v1].push_back(node(v2,c)); 91 adj[v2].push_back(node(v1,c)); 92 } 93 Bellman(s); 94 printf("%d %d\n", t[v], w[v]); 95 96 return 0; 97 }
全源最短路径
- 即求每对顶点间的最短路径
Floyd算法
- 不能含有带负权值的回路
算法实现
1 /* 2 6 8 3 0 1 1 4 0 3 4 5 0 4 4 6 1 3 2 7 2 5 1 8 3 2 2 9 3 4 3 10 4 5 3 11 */ 12 #include<cstdio> 13 #include<algorithm> 14 using namespace std; 15 const int M=220,INF=1e9; 16 int n,m,dis[M][M]; 17 18 void Floyd() 19 { 20 for(int k=0; k<n; k++) 21 for(int i=0; i<n; i++) 22 for(int j=0; j<n; j++) 23 if(dis[i][k]!=INF && dis[k][j]!=INF && dis[i][k]+dis[k][j]<dis[i][j]) 24 dis[i][j] = dis[i][k] + dis[k][j]; 25 } 26 27 int main() 28 { 29 freopen("Test.txt", "r", stdin); 30 31 int u,v,w; 32 fill(dis[0],dis[0]+M*M,INF); 33 scanf("%d%d", &n,&m); 34 for(int i=0; i<n; i++) 35 dis[i][i]=0; 36 for(int i=0; i<m; i++) 37 { 38 scanf("%d%d%d",&u,&v,&w); 39 dis[u][v] = w; 40 } 41 Floyd(); 42 for(int i=0; i<n; i++) 43 { 44 for(int j=0; j<n; j++) 45 printf("%d ", dis[i][j]); 46 printf("\n"); 47 } 48 49 return 0; 50 }