最短路算法中,精确找到最短路最常用的办法莫过于Bellman-Ford以及其优化后的SPFA算法。
Bellman-Ford算法较Dijkstar算法可以应用于含负边权的图,对于一个图G(V,E),其中V为点集,E为边集,源点s,首先定义dis数组,dis[i]为i到s的最短距离。枚举每一条边(u,v),如果dis[u]+w[u,v]<dis[v],就更新dis[v]的值。重复这个步骤,在不存在负环的情况下最多执行N-1次,所以在寻找最短路完成后枚举所有边查看是否有dis[u]+w[u,v]<dis[v]的情况,如果有,那么就存在负环,否则dis[t]就是最短路径。
Bellman-Ford算法实现代码:
bool bellman-ford()
{
bool flag;
for(int i=1;i<=n;++i)
{
flag=false;
for(int j=0;j<m;++j)
{
int x=edge[j].from;
int y=edge[j].to;
int z=edge[j].w;
if(dis[x]+z<dis[y])
{
dis[y]=dis[x]+z;
flag=true;
}
}
if(!flag)
break;
if(i==n&&flag)
return false;
}
return true;
}
SPFA则是Bellman-Ford算法加入队列优化后的算法,并不是每个点都需要松弛,因此也减少了不必要的计算,也就很大程度上减小了复杂度,可以证明正常情况下每个点进队次数不会超过两次。其思想是:初始时队列中只有源点s,然后用队列里的点来更新dis数组的值,设置一个vis数组来表示每个点是否被询问过,如果一个点被询问两次,那么说明图中存在负环。
SPFA求最短路代码:
bool SPFA(int st) 返回真为存在负环,假为求最短路成功
{
memset(dis,INF,sizeof(dis));
vis[st]=1;
q.push(st);
dis[st]=0;
while(!q.empty())
{
int u=q.front();
q.pop();
vis[u]=0;
for(int i=head[u];i;i=nxt[i])
{
int v=to[i];
if(dis[v]>dis[u]+w[i])
{
dis[v]=dis[u]+w[i];
if(!vis[v])
{
vis[v]=1;
q.push(v);
}
else
return true;
}
}
}
return false;
}