最短路

写在前面的话:写写复习下它,过久没怎么写这类题了ios

文章部份内容出自《算法竞赛进阶指南》算法

单源最短路径

这种问题就是说给一张有向图,以某一个节点(通常为1号节点),记录下其余每个点数组

到达这个1号节点的最短路径的长度。网络

经常使用算法:Dijkstra,Bellman-Ford,SPFA(本质上是Bellman-Ford)闭包

Dijkstra算法

基本思路:

它是基于贪心算法的一种方案,只能用在全部边长度非负的图。由于若z为负,全局最小值不可能被其余点优化

更新了。其中,第一步选出来的x已经知足--dist[x]是起点到x点的最短路径。本质上,咱们在不断寻找全局spa

最小值在进行标记和拓展,最后就获得起点1到每一个节点的最短路。code

过程:

1.初始化dist[i]数组,即原点到i点的距离,dist[1]=0,其余点初始化到原点距离为+∞;blog

2.找出一个未被标记的且dist[x]最小的点x,再标记节点x;队列

3.扫描x的全部出边,若dist[y]>dist[x]+z,就更新dist[y] = dist[x]+z;

4.重复2--3,直到全部点被标记为止

固然咱们能够发现,在找全局最小值的时候,代码能够继续优化,即找这个最小值能够用二叉堆去找,

复杂度从O($n^{2}$) 进化成 O($m log n$)。

#include<iostream> #include<queue> #include<cstring> #include<cstdio> using namespace std; const long MAXN=1000010; long n,m; long head[MAXN],Next[MAXN],ver[MAXN],edge[MAXN],tot=0; priority_queue< pair<long,long> > q; inline void add(long x,long y,long z){ ver[++tot]=y,edge[tot]=z,Next[tot]=head[x],head[x]=tot; } bool v[MAXN]; long dist[MAXN]; inline void dijkstra(){ for(long i=1;i<=n;i++) dist[i]=999999; memset(v,0,sizeof v); dist[1]=0; q.push(make_pair(0,1)); while(q.size()){ long x=q.top().second; q.pop(); if(v[x]) continue; v[x]=1; for(long i=head[x];i;i=Next[i]) { long y=ver[i],z=edge[i]; if(dist[y]>dist[x]+z){ dist[y]=dist[x]+z; q.push(make_pair(-dist[y],y)); } } } } int main(){ scanf("%ld%ld",&n,&m); long x,y,z; for(long i=1;i<=m;i++){ scanf("%ld%ld%ld",&x,&y,&z); add(x,y,z); } dijkstra(); for(long i=1;i<=n;i++) printf("%ld\n",dist[i]); }

Bellman-Ford算法

基本思路

它是基于迭代的思想去考虑,即便全部边(x,y,z)知足三角形不等式:d[y]<=d[x]+z

以下图:

 

过程:

1.扫描全部边,若dist[y]>dist[x]+z,就使dist[y]=dist[x]+z;

2.重复上述步骤直到没有更新发生为止。

复杂度高达O(nm),但它优于dijkstra的一点,是它的边权能够为负值。

SPFA算法

基本思路:

spfa在国际上通称”队列优化的Bellman-Ford算法“,它用队列保存了待扩展的节点,每一次的入队

至关于完成一次更新,使得节点慢慢收敛,即松弛,稀疏图效率高。

由于对于一个从x到y的边,节点x尚未松弛过,那y就不必松弛,因此咱们用队列记录松弛过的点,

避免了bellman-ford中的冗余松弛操做。

过程:

1.创建一个队列,初始入队节点1;

2.取出队头,并扫描全部出边,若dist[y]>dist[x]+z,则更新dist[y]=dist[x]+z,并判断y是否在队列中,若

不在,则将y入队;

3.重复上述步骤直至队列为空。

用简略语言概述就是,咱们有一个松弛点x,尝试x能到达的点y,若y能够被松弛,就更新dist[y],在

这个条件下,若y还没入队,那就把这个松驰过的点入队,直到队列为空。

#include<iostream> #include<queue> #include<cstring> #include<cstdio> using namespace std; const long MAXN=1000010; long n,m; long head[MAXN],Next[MAXN],ver[MAXN],edge[MAXN],tot=0; queue <long> q; inline void add(long x,long y,long z){ ver[++tot]=y,edge[tot]=z,Next[tot]=head[x],head[x]=tot; } bool v[MAXN]; long dist[MAXN]; inline void spfa(){ for(long i=1;i<=n;i++) dist[i]=999999; memset(v,0,sizeof v); dist[1]=0; q.push(1); v[1]=1; while(q.size()){ long x=q.front(); q.pop(); v[x]=0; for(long i=head[x];i;i=Next[i]) { long y=ver[i],z=edge[i]; if(dist[y]>dist[x]+z){ dist[y]=dist[x]+z; if(v[y]==0) q.push(y),v[y]=1; } } } } int main(){ scanf("%ld%ld",&n,&m); long x,y,z; for(long i=1;i<=m;i++){ scanf("%ld%ld%ld",&x,&y,&z); add(x,y,z); } spfa(); for(long i=1;i<=n;i++) printf("%ld\n",dist[i]); }

特别地,当咱们要去断定负环的时候,咱们能够用一个数组c[i]表示1到i的最短路径上点的个数,每一次松弛x-y时,

咱们就用c[y]=c[x]+1,当c[y]>n时,即存在负环。

注:

其实对比dijkstra和spfa,咱们能够发现,dijkstra针对每个最小距离点进行扩展,

spfa则是经过迭代思想将全部边进行扩展,天然spfa的复杂度会高于dijkstra,可是spfa同时也具备

能够更加容易加入各类其余算法和对负权的边,负环的处理。

舒适提示,面对一个不存在负权边的图最好用dijkstra,由于spfa易被恶意卡掉。

任意两点间的最短路径

Floyd算法

基本思路:

它是一种基于动态规划的算法,咱们能够用D[k,i,j]来表示i通过编号不超过k的节点后到达j的最短路,因此

咱们能够将其划分为两个子问题,一是congi通过编号不超过k-1的节点到j,或者从i通过k节点到达j。

则 D[k,i,j]=min(D[k-1,i,j],D[k-1,i,k]+D[k-1,k,j])

因此k是阶段,应该放在最外层

咱们也能够用D保存邻接矩阵,省略k这一维,即:

D[i,j]=min(D[i,j],D[i,k]+D[k,j])

也能够理解为从i到j,看看目前的状况好,仍是再通过一个中间点k更好。

过程:

这就再也不赘述了,太过简单......

 

#include<iostream> #include<cmath> using namespace std; const long MAXN=10010; long dist[MAXN][MAXN]; long n,m; int main(){ cin>>n>>m; for(long i=1;i<=n;i++) for(long j=1;j<=n;j++) dist[i][j]=999999; for(long i=1;i<=n;i++) d[i][i]=0; long x,y,z; for(long i=1;i<=m;i++){ cin>>x>>y>>z; if(dist[x][y]>z) dist[x][y]=z; } for(long k=1;k<=n;k++) for(long i=1;i<=n;i++) for(long j=1;j<=n;j++) dist[i][j]=min(dist[i][j],dist[i][k]+dist[k][j]); for(long i=1;i<=n;i++){ for(long j=1;j<=n;j++) cout<<dist[i][j]<<" "; cout<<endl; } }

范围,数值最大值能够自行更改

传递闭包

在交际网络中存在若干元素和若干对二元关系,关系具备传递性,请推导出尽可能多的元素间的关系----传递闭包

易得,咱们能够用floyd解决此类问题

#include<iostream> #include<cmath> using namespace std; const long MAXN=10010; bool dist[MAXN][MAXN]; long n,m; int main(){ cin>>n>>m; for(long i=1;i<=n;i++) dist[i][i]=1; long x,y,z; for(long i=1;i<=m;i++){ cin>>x>>y; dist[x][y]=dist[y][x]=1; } for(long k=1;k<=n;k++) for(long i=1;i<=n;i++) for(long j=1;j<=n;j++) dist[i][j]|=dist[i][k]&dist[k][j]; }
相关文章
相关标签/搜索