一提起最短路,各位oier会想到什么呢?
floyd,spfa,dij,或是bellman-ford?
其实,只要学会一种算法,大部分最短路问题就能很快解决了。
他就是堆优化的dijkstra。
首先,先讲一下dij是怎么求最短路的。
Dijkstra是基于一种贪心的策略,首先用数组dis记录起点到每个结点的最短路径,再用一个数组保存已经找到最短路径的点
然后,从dis数组选择最小值,则该值就是源点s到该值对应的顶点的最短路径,并且把该点记为已经找到最短路
此时完成一个顶点,再看这个点能否到达其它点(记为v),将dis[v]的值进行更新
不断重复上述动作,将所有的点都更新到最短路径
这种算法实际上是O(n^2)的时间复杂度,但我们发现在dis数组中选择最小值时,我们可以用一些数据结构来进行优化。
其实我们可以用STL里的堆来进行优化,堆相对于线段树以及平衡树有着常数小,码量小等优点,并且堆的一个妙妙的性质就是可以在nlogn的时限内满足堆顶是堆内元素的最大(小)值,之不正是我们要的嘛?
但是呢,dij处理不了负边,所以当题目出现负边时,dij就不能用了。
但反过来说,只要题目没负边,SPFA是一定会被卡的!
下面上代码:
#include<bits/stdc++.h> using namespace std; #define maxn 10005 #define maxm 500005 #define INF 1234567890 inline int read() { int x=0,k=1; char c=getchar(); while(c<'0'||c>'9'){if(c=='-')k=-1;c=getchar();} while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar(); return x*k; } struct Edge { int u,v,w,next; }e[maxm]; int head[maxn],cnt,n,m,s,vis[maxn],dis[maxn]; struct node { int w,now; inline bool operator <(const node &x)const //重载运算符把最小的元素放在堆顶(大根堆) { return w>x.w;//这里注意符号要为'>' } }; priority_queue<node>q; //优先队列,其实这里一般使用一个pair,但为了方便理解所以用的结构体 inline void add(int u,int v,int w) { e[++cnt].u=u; //这句话对于此题不需要,但在缩点之类的问题还是有用的 e[cnt].v=v; e[cnt].w=w; e[cnt].next=head[u]; //存储该点的下一条边 head[u]=cnt; //更新目前该点的最后一条边(就是这一条边) } //链式前向星加边 void dijkstra() { for(int i=1;i<=n;i++) { dis[i]=INF; } dis[s]=0; //赋初值 q.push((node){0,s}); while(!q.empty()) //堆为空即为所有点都更新 { node x=q.top(); q.pop(); int u=x.now; //记录堆顶(堆内最小的边)并将其弹出 if(vis[u]) continue; //没有遍历过才需要遍历 vis[u]=1; for(int i=head[u];i;i=e[i].next) //搜索堆顶所有连边 { int v=e[i].v; if(dis[v]>dis[u]+e[i].w) { dis[v]=dis[u]+e[i].w; //松弛操作 q.push((node){dis[v],v}); //把新遍历到的点加入堆中 } } } } int main() { n=read(),m=read(),s=read(); for(int i=1,x,y,z;i<=m;i++) { x=read(),y=read(),z=read(); add(x,y,z); } dijkstra(); for(int i=1;i<=n;i++) { printf("%d ",dis[i]); } return 0; }
谢谢大家!