3种求最短路的方法

来源:互联网 发布:秦始皇活着吗 知乎 编辑:程序博客网 时间:2024/05/16 07:29

最短路

这几天做题,碰见了很多最短路的题目,于是就在博客里发一下3种比较常用的算法。
In order to 方便以后自己看……

备注:n为点数,m为边数

Dijkstra

复杂度O(n^2)

适用于没有负权边的图,主要作用是单源求最短路

特点:复杂度稳定

思想:

①先拓展出起始点v0

②把与v'相邻的所有未拓展出的点的d更新(设拓展出来的是v'' 则d[v'']=min(d[v''],d[v']+dis[v'][v'']))

③找出已拓展出来的所有点相邻的距离最短的点v'(此处可以堆优化),并且拓展出来

④循环②③直到所有点都被拓展出来


代码(没有堆优化且写法繁琐,使用了邻接表)(以后会了,会补上有堆优化的)

#include <cstring>#include <iostream>#include <cstdio>#include <algorithm>#include <cmath>#include <queue>#include <vector>#include <stack>#define inf 0x3fusing namespace std;void fre(){freopen(".in","r",stdin);freopen(".out","w",stdout);}const int maxn=1000,maxm=10000;struct Edge{int v,next,l;}edge[maxm];int head[maxn],cnt=1;int n,m;void addedge(int a,int b,int L){edge[cnt].next=head[a];edge[cnt].v=b;edge[cnt].l=L;head[a]=cnt++;}int dis[maxn];bool vis[maxn];void dijkstra(int start,int d[]){d[start]=0;vis[start]=true;for(int i=head[start];i!=-1;i=edge[i].next){int y=edge[i].v,dist=edge[i].l;d[y]=dist;}for(int k=1;k<n;k++){int MIN=0;for(int i=1;i<=n;i++){if(!vis[i] && d[i]<d[MIN]){MIN=i;}}vis[MIN]=true;for(int i=head[MIN];i!=-1;i=edge[i].next){int x=MIN,y=edge[i].v,dist=edge[i].l;if(!vis[y] && d[x]+dist<d[y]){d[y]=d[x]+dist;}}}}void init(){memset(head,-1,sizeof(head));memset(dis,inf,sizeof(dis));}int main(){init();scanf("%d%d",&n,&m);for(int i=1;i<=m;i++){int x,y,L;scanf("%d%d%d",&x,&y,&L);addedge(x,y,L);addedge(y,x,L);}int start;scanf("%d",&start);dijkstra(start,dis);for(int i=1;i<=n;i++)printf("%d %d\n",i,dis[i]);return 0;}

Floyd

复杂度O(n^3)

适用于求出每两个点的最短路距离

主要代码:

for(int k=1;k<=n;k++)for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)f[i][j]=min(f[i][j],f[i][k]+f[k][j]);

思想:

此(上面代码)中有真意,欲辨已忘言!


代码

#include <cstring>#include <iostream>#include <cstdio>#include <algorithm>#include <cmath>#include <queue>#include <vector>#include <stack>#define inf 0x3f3f3f3fusing namespace std;void fre(){freopen(".in","r",stdin);freopen(".out","w",stdout);}const int maxn=1000;int f[510][510];int gmin(int a,int b){return a<b ? a: b;}int n,m;int main(){//fre();memset(f,inf,sizeof(f));scanf("%d%d",&n,&m);for(int i=1;i<=m;i++){int x,y,l;scanf("%d%d%d",&x,&y,&l);f[x][y]=f[y][x]=l;}for(int k=1;k<=n;k++)for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)f[i][j]=gmin(f[i][j],f[i][k]+f[k][j]);for(int i=1;i<=n;i++)f[i][i]=0;for(int i=1;i<=n;i++){for(int j=1;j<=n;j++)printf("%d ",f[i][j]);puts("");}return 0;}

SPFA

复杂度:O(k*m)(k为所有点的平均进队次数)

适用于有负边的图,但是不能有负环,主要作用是单源求最短路

特点:复杂度不稳定

基本思想:

①弹出队头的点,访问它所连的点

②如果d[y]>d[x]+l,则执行

d[y]=d[x]+l

再看y是否在队列中,如果不再就把y进队

③知道队列为空

代码(用了系统队列)

</pre><p><pre name="code" class="cpp">#include <bits/stdc++.h>#define inf 0x3fusing namespace std;void fre(){freopen(".in","r",stdin);freopen(".out","w",stdout);}const int maxn=510,maxm=10010;struct Edge{int  to,next,L;}edge[maxn];int head[maxn],cnt=0;int n,m;void addedge (int u,int v,int l){cnt++;edge[cnt].to=v;edge[cnt].next=head[u];edge[cnt].L=l;head[u]=cnt;}int dis[maxn];bool vis[maxn];queue <int> que;int c[maxn];void spfa(int start,int d[]){que.push(start);vis[start]=true;c[start]=1;d[start]=0;while(!que.empty()){int x=que.front();que.pop();vis[x]=false;for(int i=head[x];i!=-1;i=edge[i].next){int y=edge[i].to,l=edge[i].L;if(d[y]>d[x]+l ){d[y]=d[x]+l;if(!vis[y]){que.push(y);vis[y]=true;c[y]++;if(c[y]>n){puts("-1");return ;}}}}}}void init(){memset(head,-1,sizeof(head));memset(dis,0x3f,sizeof(dis));}int main(){init();scanf("%d%d",&n,&m);for(int i=1;i<=m;i++){int x,y,l;scanf("%d%d%d",&x,&y,&l);addedge(x,y,l);addedge(y,x,l);}int X;scanf("%d",&X);spfa(X,dis);for(int i=1;i<=n;i++)printf("%d\n",dis[i]);return 0;}




0 0
原创粉丝点击