SPFA求有负边的单源点最短路
Bellman-ford算法
最短路经过的路径条数,小于图中点的个数
若大于等于点的个数,则意味着存在某点被重复经过
当松弛边(u,v)时,如果dis[u]已经时最短路且u在v的最短路上,则松弛结束后dis[v]也是最短路,并且从此以后其dis的值不会发生变化。
对图中每一条边都进行松弛,由于最短路的路径条数小于点的个数,故松弛 点数-1轮即可
第i轮松弛完毕后,所有经过i跳变的最短路都被确定
(以上我也没懂)
for(int i = 1;i <= n;i++)
{
dis[i] = INF;
pre[i] = 0;
}
dis[s] = 0;
for(int k = 1;k<=n;k++)
for(int i = 1;i <= m;i++)
if(dis[v[i]] > dis[u[i]] + w[i])
{
dis[v[i]] = dis[u[i]] + w;
pre[v[i]] = u[i];
}
时间复杂度O(nm)
SPFA shortest path faster algorithm
观察Bellman-ford算法的松弛过程,松弛擦做仅仅发生在最短路径前导结点中的已经成功松弛过的节点上。
第一轮,与s临街的点被松弛–>最短路径上的第一条边
第二轮与第一轮被松弛的相邻接的点被松弛->最端路径上的第二条边
只做有效的松弛操作:
建一个队列
队列中存储被成功松弛的点
每次从队首取店并松弛其邻接点
如果邻接点松弛成功就将其加入队列
思考:
存在重复入队问题, 数组计录是否已经在队列中
void spfa(int s )
{
for(int i = 1;i <= n;i++) cna[i] = 0,dis[i] = inf;
dis[s] = 0;
can[s] = 1;
queue<int> p;
p.push(s);
while(!p.empty())
{
int now = p.front();
p.pop();
for(int i = point[now];i;i=nxt[i])
if(dis[v[i]] > dis[now] + len[i])
{
dis[v[i]] = dis[now] + len[i];
pre[v[i]] = now;
if(!can[v[i]])//防止重复入队
{
can[v[i]] = 1;
p.push(v[i]);
}
}
can[now] = 0;//出队标为0
}
}
时间复杂度,平均为o(km)
k是一个小于n的小常数
负权环路
Bellman-ford算法及其队列优化可以解决负权边的问题,而且SPFA的时间复杂度较为优秀。
S不可达,存在负权环路的时候,最短路是不存在的。
如果有负权环路,那么每走一圈负权环路,最短路长度都会减小,无限循环则会无线小下去,因此时不存在最短路的。
需要判断途中是否含有负环
如果存在负环,那么最短录经过的边数会打与等于n
一些边被松弛的次数会大于等于n
如果在地n批次松弛操作时还存在能够成功被松弛的边做说明图中存在负环
//修改后的Bellman-ford算法
// 初始化
for(int i = 1;i <=n;i++)
{
dis[i] = inf; //#define inf 1e9
pre[i] = 0;
}
dis[s] = 0;
for(int k = 1;k < n;k++)
for(int i = 1;i <= m;i++)
if(dis[v[i]] > dis[u[i]] + w[i])
{
dis[v[i]] = dis[u[i]] + w;
pre[v[i]] = u[i];
}
//在进行n-1轮松弛之后,再进行一次松弛,如果有边能够被松弛成功,则说明存在负环
for(int ii = 1;i <=m;i++)
if(dis[v[i]] > dis[u[i]] + w[i])
{
//存在负环
}
修改后的SPFA算法
cnt[x]表示到点x当前最短路上的边数
每次更新最短路时更新cnt[v[,如果某一时刻cnt[v]大于等于n的话,说明图中存在负环
void spfa(int s)
{
//初始化
for(int i = 1;i <= n;i++) cna[i] = 0,dis[i] = inf,cnt[i] = 0;
dis[s] = 0;
can[s] =1;
p.push(s);
while(!p.empty())
{
int now = p.front();
p.pop();
for(int i = point[now];i;i=nxt[i])
if(dis[v[i]] > dis[u[i]] + w[i])
{
dis[v[i]] = dis[now] + w[i];
cnt[v[i]] = cnt[u[i]] + 1;
if(cnt[v[i]] >= n)
{ //负环
}
pre[v[i]]=now;
if(!can[v[i]])
{
can[v[i]] = 1;
p.push(i0;
}
}
can[now]=0;
}
}