1.最短路是什么?
顾名思义,最短路就是求一个点到另一个点最短的路径,一半分为单源最短路和多源最短路。
2.最短路问题如何解决?
多源最短路问题:
多源最短路基本只有一条路可走(当询问次数很小时例外),那就是Flyod算法了。
Flyod:
枚举k,i,j记住k一定要在最外层就好,每一次对于一个点进行松弛操作(就是更新dis),那么时间复杂度也是比较显然的O(n^3)
例题:luoguP2910寻宝之路
附上该题代码:
1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 int a[10005],f[105][105],ans; 5 void floyd(int n) 6 { 7 for(int k=1;k<=n;k++) 8 { 9 for(int i=1;i<=n;i++) 10 { 11 for(int j=1;j<=n;j++) 12 { 13 f[i][j]=min(f[i][j],f[i][k]+f[k][j]); 14 } 15 } 16 } 17 } 18 int main() 19 { 20 int n,m; 21 scanf("%d%d",&n,&m); 22 for(int i=1;i<=m;i++) 23 { 24 scanf("%d",&a[i]); 25 } 26 for(int i=1;i<=n;i++) 27 { 28 for(int j=1;j<=n;j++) 29 { 30 scanf("%d",&f[i][j]); 31 } 32 } 33 floyd(n); 34 for(int i=2;i<=m;i++) 35 { 36 ans+=f[a[i-1]][a[i]]; 37 } 38 printf("%d",ans); 39 return 0; 40 }
单源最短路问题:
1.Dijkstra算法:(不能处理负边权!!!)
Dijkstra算法的原理很简单,每一次取离原点最近的一个点,这样它已经是最短路,再用他来更新与他相邻的点的dis,不断更新下去即可。
那么终止条件就是不会有可以更新的点,时间复杂度是(N^2),找最近点可以用堆优化,那么就可以优化为((n+m)logn)
Dijkstra是一个相当稳定的算法,基本不会被卡死
例题:luoguP3371最短路(弱化版) luoguP4779最短路(标准版)
附上两道题代码:
1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 struct EDGE 5 { 6 int to,nxt,v; 7 }edge[500005]; 8 int head[10005],cnt,vis[10005],n,m,s; 9 long long dis[10005]; 10 void add(int a,int b,int c) 11 { 12 edge[++cnt].to=b; 13 edge[cnt].v=c; 14 edge[cnt].nxt=head[a]; 15 head[a]=cnt; 16 } 17 void dijkstra() 18 { 19 for(int i=1; i<=n; i++) 20 { 21 dis[i]=2147483647; 22 } 23 dis[s]=0; 24 long long minn=2147483647; 25 int cur=s; 26 while(vis[cur]==0) 27 { 28 vis[cur]=1; 29 for(int i=head[cur]; i; i=edge[i].nxt) 30 { 31 dis[edge[i].to]=min(dis[cur]+edge[i].v,dis[edge[i].to]); 32 } 33 minn=2147483647; 34 for(int i=1;i<=n;i++) 35 { 36 if(vis[i]==0&&minn>dis[i]) 37 { 38 minn=dis[i]; 39 cur=i; 40 } 41 } 42 } 43 } 44 int main() 45 { 46 scanf("%d%d%d",&n,&m,&s); 47 for(int i=1; i<=m; i++) 48 { 49 int a,b,c; 50 scanf("%d%d%d",&a,&b,&c); 51 add(a,b,c); 52 } 53 dijkstra(); 54 for(int i=1; i<=n; i++) 55 { 56 printf("%lld ",dis[i]); 57 } 58 return 0; 59 }
1 #include<bits/stdc++.h> 2 using namespace std; 3 int fr[100010],tl,n,m,x,y,z,s;; 4 bool b[100010]; 5 long long d[100010]; 6 struct EDGE 7 { 8 int to,nxt,v; 9 } edge[200005]; 10 void add(int x,int y,int w) 11 { 12 edge[++tl].to=y; 13 edge[tl].v=w; 14 edge[tl].nxt=fr[x]; 15 fr[x]=tl; 16 } 17 priority_queue< pair<int,int> > q; 18 void dijkstra() 19 { 20 for(int i=1; i<=n; i++) 21 { 22 d[i]=1e10; 23 } 24 d[s]=0; 25 q.push(make_pair(0,s)); 26 while(q.empty()==0) 27 { 28 int x=q.top().second; 29 q.pop(); 30 if(b[x]==1) continue; 31 b[x]=1; 32 for(int i=fr[x]; i; i=edge[i].nxt) 33 { 34 int y=edge[i].to,l=edge[i].v; 35 if(d[y]>d[x]+l) 36 { 37 d[y]=d[x]+l; 38 q.push(make_pair(-d[y],y)); 39 } 40 } 41 } 42 } 43 int main() 44 { 45 scanf("%d%d%d",&n,&m,&s); 46 for(int i=1; i<=m; i++) 47 { 48 scanf("%d%d%d",&x,&y,&z); 49 add(x,y,z); 50 } 51 dijkstra(); 52 for(int i=1; i<=n; i++) printf("%lld ",d[i]); 53 return 0; 54 }
2.SPFA(可以处理负边权!!!)
虽然说最近SPFA受到了针对,出题人利用SPFA的不稳定性轻松将其卡死,但是SPFA在解决负环和差分约束等问题还是有着很重大的意义
SPFA的原理更简单,就是开一个队列,每一次遇到dis可以更新的点就更新dis,如果它不再队列里就加入队列,当队列为空就得到了最短路。
而判断是否存在负环也很显然,一个点被更新了n-1次,那么就说明存在负环了。
例题:luoguP2299体委争夺战附上该题代码:
1 #include<cstdio> 2 #include<queue> 3 #define maxm 200000 4 #define maxn 2500 5 #define inf 1e7 6 using namespace std; 7 8 int n,m,cnt; 9 struct EDGE 10 { 11 int nxt,to,v; 12 }; 13 EDGE edge[2*maxm+5]; 14 int head[maxn+5],dis[maxn+5]; 15 bool vis[maxn+5]; 16 queue<int>q; 17 18 void add(int x,int y,int z) 19 { 20 edge[++cnt].to=y; 21 edge[cnt].v=z; 22 edge[cnt].nxt=head[x]; 23 head[x]=cnt; 24 } 25 void SPFA() 26 { 27 int s=1; 28 dis[s]=0; 29 for(int i=2;i<=n;i++) 30 { 31 dis[i]=inf; 32 } 33 q.push(s); 34 while(!q.empty()) 35 { 36 s=q.front(); 37 vis[s]=0; 38 q.pop(); 39 for(int i=head[s];i;i=edge[i].nxt) 40 { 41 if(dis[edge[i].to]>dis[s]+edge[i].v) 42 { 43 dis[edge[i].to]=dis[s]+edge[i].v; 44 if(vis[edge[i].to]==0) 45 { 46 vis[edge[i].to]=1; 47 q.push(edge[i].to); 48 } 49 } 50 } 51 } 52 } 53 int main() 54 { 55 scanf("%d%d",&n,&m); 56 for(int i=1;i<=m;i++) 57 { 58 int x,y,z; 59 scanf("%d%d%d",&x,&y,&z); 60 add(x,y,z); 61 add(y,x,z); 62 } 63 SPFA(); 64 printf("%d ",dis[n]); 65 return 0; 66 }
总而言之,对于不同的题目要选择最优的方法,并不存在哪个算法更重要,算法都一样好,就是看用的人了。。。