最短路
我 讨 厌 静 电!!!
最短路分类及时间复杂度
Floyd算法
是用来求任意两个结点之间的最短路的。
复杂度比较高,但是常数小,容易实现。(我会说只有三个 for
吗?)
适用于任何图,不管有向无向,边权正负,但是最短路必须存在。(不能有个负环)
三维的数组能够优化成二维数组进行计算
如图是三维数组思路及模板 暴力且无脑66
1 #include <bits/stdc++.h> 2 #define endl '\n' 3 #define x first 4 #define y second 5 #define int long long 6 #define Tang ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); 7 8 using namespace std; 9 10 const int N=210; 11 const int INF=1e9; 12 const int mod=1e9+7; 13 14 int n,m,k; 15 int dist[N][N]; 16 17 void Floyd() //可以将三维Floyd的空间优化为二维进行计算 18 { 19 20 for(int k=1;k<=n;k++) 21 for(int i=1;i<=n;i++) 22 for(int j=1;j<=n;j++) 23 dist[i][j]=min(dist[i][j],dist[i][k]+dist[k][j]); //循环结束后dist[i][j]存的就是i到j的最短距离 24 25 } 26 27 void solve() 28 { 29 30 cin >> n >> m >> k; 31 32 for(int i=1;i<=n;i++) 33 for(int j=1;j<=n;j++) 34 if(i==j) 35 dist[i][j]=0; //到自己的距离是0 36 else 37 dist[i][j]=INF; 38 while(m--) 39 { 40 int a,b,c; 41 cin >> a >> b >> c; 42 dist[a][b]=min(dist[a][b],c); 43 } 44 45 Floyd(); 46 47 while(k--) 48 { 49 50 int a,b; 51 cin >> a >> b; 52 53 if(dist[a][b]>INF/2) 54 cout << "impossible" << endl; 55 else 56 cout << dist[a][b] << endl; 57 58 } 59 60 } 61 62 signed main() 63 { 64 Tang 65 int T=1; 66 //cin >> T; 67 while(T--) 68 solve(); 69 70 return 0; 71
72 }
松弛操作:
(上图中左到右的距离在一次松弛操作中由4更新为3)
Bellman_Ford算法
1 #include <bits/stdc++.h> 2 #define endl '\n' 3 #define x first 4 #define y second 5 #define int long long 6 #define Tang ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); 7 8 using namespace std; 9 10 const int N=510,M=10010; 11 const int INF=0x3f3f3f3f; 12 const int mod=1e9+7; 13 14 struct node 15 { 16 int a,b,c; 17 }q[M]; 18 19 int n,m,k; 20 int dist[N]; 21 int last[N]; 22 23 void Bellman_ford() 24 { 25 26 memset(dist,0x3f,sizeof dist); 27 dist[1]=0; 28 for(int i=0;i<k;i++) 29 { 30 31 memcpy(last,dist,sizeof dist); //将dist的数值存到last中 32 for(int j=0;j<m;j++) 33 { 34 auto t=q[j]; 35 dist[t.b]=min(dist[t.b],last[t.a]+t.c); //进行松弛操作 36 } 37 38 } 39 40 } 41 42 void solve() 43 { 44 45 cin >> n >> m >> k; 46 47 for(int i=0;i<m;i++) 48 { 49 50 int a,b,c; 51 cin >> a >> b >> c; 52 q[i]={a,b,c}; 53 54 } 55 56 Bellman_ford(); 57 58 if(dist[n]>INF/2) 59 cout << "impossible" << endl; 60 else 61 cout << dist[n] << endl; 62 63 } 64 65 signed main() 66 { 67 Tang 68 int T=1; 69 //cin >> T; 70 while(T--) 71 solve(); 72 73 return 0; 74 75 }
SPFA算法
只有上一次被松弛的结点,所连接的边,才有可能引起下一次的松弛操作。
很多时候我们并不需要那么多无用的松弛操作。
1 #include <bits/stdc++.h> 2 #define endl '\n' 3 #define x first 4 #define y second 5 #define int long long 6 #define Tang ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); 7 8 using namespace std; 9 10 const int N=1e5+10; 11 const int INF=0x3f3f3f3f; 12 const int mod=1e9+7; 13 14 int n,m; 15 int h[N],e[N],ne[N],w[N],idx; 16 int dist[N]; 17 bool st[N]; 18 19 void add(int a,int b,int c) 20 { 21 e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++; 22 } 23 24 int Spfa() 25 { 26 27 memset(dist,0x3f,sizeof dist); 28 dist[1]=0; 29 30 queue<int> q; 31 q.push(1); 32 st[1]=true; 33 34 while(q.size()) 35 { 36 37 int t=q.front(); 38 q.pop(); 39 40 st[t]=false; 41 42 for(int i=h[t];i!=-1;i=ne[i]) 43 { 44 int j=e[i]; 45 if(dist[j]>dist[t]+w[i]) 46 { 47 dist[j]=dist[t]+w[i]; 48 if(!st[j]) 49 { 50 q.push(j); 51 st[j]=true; 52 } 53 } 54 } 55 56 } 57 return dist[n]; 58 } 59 60 void solve() 61 { 62 63 cin >> n >> m; 64 65 memset(h,-1,sizeof h); 66 67 while(m--) 68 { 69 70 int a,b,c; 71 cin >> a >> b >> c; 72 add(a,b,c); 73 74 } 75 76 if(Spfa()>INF/2) 77 cout << "impossible" << endl; 78 else 79 cout << Spfa() << endl; 80 81 } 82 83 signed main() 84 { 85 Tang 86 int T=1; 87 //cin >> T; 88 while(T--) 89 solve(); 90 91 return 0; 92 93 }
并常常用SPFA来判断负环是否存在
1 #include <bits/stdc++.h> 2 #define endl '\n' 3 #define x first 4 #define y second 5 #define int long long 6 #define Tang ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); 7 8 using namespace std; 9 10 const int N=2010; 11 const int M=10010; 12 const int INF=0x3f3f3f3f; 13 const int mod=1e9+7; 14 15 int n,m; 16 int h[N],e[M],ne[M],w[M],idx; 17 int dist[N],cnt[N]; 18 bool st[N]; 19 20 void add(int a,int b,int c) 21 { 22 e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++; 23 } 24 25 bool spfa() 26 { 27 28 queue<int> q; 29 30 for(int i=1;i<=n;i++) 31 { 32 st[i]=true; 33 q.push(i); 34 } 35 36 while(q.size()) 37 { 38 39 int t=q.front(); 40 q.pop(); 41 42 st[t]=false; 43 44 for(int i=h[t];i!=-1;i=ne[i]) 45 { 46 int k=e[i]; 47 if(dist[k]>dist[t]+w[i]) 48 { 49 dist[k]=dist[t]+w[i]; 50 cnt[k]=cnt[t]+1; 51 52 if(cnt[k]>=n) 53 return true; 54 if(!st[k]) 55 { 56 q.push(k); 57 st[k]=true; 58 } 59 } 60 } 61 62 } 63 return false; 64 } 65 66 void solve() 67 { 68 69 cin >> n >> m; 70 71 memset(h,-1,sizeof h); 72 73 while(m--) 74 { 75 76 int a,b,c; 77 cin >> a >> b >> c; 78 add(a,b,c); 79 80 } 81 82 if(spfa()) 83 cout << "Yes" << endl; 84 else 85 cout << "No" << endl; 86 87 } 88 89 signed main() 90 { 91 Tang 92 int T=1; 93 //cin >> T; 94 while(T--) 95 solve(); 96 97 return 0; 98 99 }
Dijkstra算法
1 #include <bits/stdc++.h> 2 #define endl '\n' 3 #define x first 4 #define y second 5 #define int long long 6 #define Tang ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); 7 8 using namespace std; 9 10 const int N=510; 11 const int INF=0x3f3f3f3f; 12 const int mod=1e9+7; 13 14 int n,m; 15 int g[N][N]; //邻接矩阵储存所有边权 16 int dist[N]; //存储1到各个点的距离 17 bool st[N]; //标记,判断是否走过 18 19 void Dijkstra() 20 { 21 22 memset(dist,0x3f,sizeof dist); //清空距离数组 23 dist[1]=0; 24 25 for(int i=0;i<n-1;i++) 26 { 27 28 int t=-1; 29 30 for(int j=1;j<=n;j++) 31 if(!st[j] && (t==-1 || dist[t]>dist[j])) //选取一个最短路的点放入dist数组中 32 t=j; 33 for(int j=1;j<=n;j++) 34 dist[j]=min(dist[j],dist[t]+g[t][j]); //将刚放入dist数组的点遍历进行松弛操作 35 36 st[t]=true; 37 38 } 39 if(dist[n]>0x3f3f3f3f/2) 40 cout << -1 << endl; 41 else 42 cout << dist[n] << endl; 43 44 } 45 46 void solve() 47 { 48 49 cin >> n >> m; 50 memset(g,0x3f,sizeof g); 51 52 for(int i=0;i<m;i++) 53 { 54 int a,b,c; 55 cin >> a >> b >> c; 56 57 g[a][b]=min(g[a][b],c); //求最短路时只需要存储两点之间最短的一条边就可以 58 } 59 60 Dijkstra(); 61 62 } 63 64 signed main() 65 { 66 Tang 67 int T=1; 68 //cin >> T; 69 while(T--) 70 solve(); 71 72 return 0; 73 74 }
1 #include <bits/stdc++.h> 2 #define endl '\n' 3 #define x first 4 #define y second 5 #define int long long 6 #define Tang ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); 7 8 using namespace std; 9 10 const int N=1e6+10; 11 const int INF=0x3f3f3f3f; 12 const int mod=1e9+7; 13 14 typedef pair<int,int> PII; 15 16 int n,m; 17 int h[N],e[N],ne[N],w[N],idx; 18 int dist[N]; 19 bool st[N]; 20 21 void add(int a,int b,int c) //数组模拟链表加入操作 22 { 23 e[idx]=b,ne[idx]=h[a],w[idx]=c;h[a]=idx++; 24 } 25 26 int Dijkstra() 27 { 28 29 memset(dist,0x3f,sizeof dist); 30 dist[1]=0; 31 priority_queue<PII,vector<PII>,greater<PII>> q; //建立小根堆 32 q.push({0,1}); //first是dist[i],second是节点i 33 34 while(q.size()) //当队列不空 35 { 36 37 auto t=q.top(); //每次取堆顶元素进行操作并将元素弹出 38 q.pop(); 39 40 int dis=t.first,ver=t.second; 41 42 if(st[ver]) 43 continue; 44 st[ver]=true; 45 46 for(int i=h[ver];i!=-1;i=ne[i]) //进行松弛操作 47 { 48 int j=e[i]; 49 if(dist[j]>dist[ver]+w[i]) 50 { 51 dist[j]=dist[ver]+w[i]; //更新该点的最短路的dist值 52 q.push({dist[j],j}); //将新进入的最短路的点放入队列中 53 } 54 55 } 56 57 } 58 if(dist[n]>0x3f3f3f3f/2) 59 return -1; 60 else 61 return dist[n]; 62 63 } 64 65 void solve() 66 { 67 68 cin >> n >> m; 69 70 memset(h,-1,sizeof h); 71 72 while(m--) 73 { 74 75 int a,b,c; 76 cin >> a >> b >> c; 77 add(a, b, c); 78 79 } 80 81 cout << Dijkstra() << endl; 82 83 } 84 85 signed main() 86 { 87 Tang 88 int T=1; 89 //cin >> T; 90 while(T--) 91 solve(); 92 93 return 0; 94 95 }