关于SPFA
SPFA 算法是 Bellman-Ford算法 的队列优化算法的别称,通常用于求含负权边的单源最短路径,以及判负权环。
作用
1.最短路
该算法的最坏复杂度为 O(VE),可以被构造数据卡掉,所以说,关于spfa—他死了
所以说,跑正权图最好还是用Dijkstra
此算法朴素时间复杂度 O( n^2),堆优化O((m+n)logn)
但是我想说,我就是想用spfa,你咬我呀
怕被卡就不要使用朴素的spfa,可以优化一点
以下我介绍一下几种玄学优化
- SLF(Small Label First)优化。
该优化使用双端队列duque。基本操作可以度娘,在此不予与介绍
对一个要加入队列的点u,如果dis[u] 小于队首元素v的dis[v],那么就就加入到队首元素,否则加入到队尾。
核心代码
if(q.empty()||dis[to]>dis[q.front()])
q.push_back(to);
else q.push_front(to);
- LLL(Large Label Last)优化
该优化其实并不常用,不想被卡其实第一个已经够了 ,但是还是说一下思想
对每个要出对的元素u,比较dis[u]和队列中dis的平均值,如果dis[u]更大,那么将它弹出放到队尾,取队首元素在进行重复判断,直至存在dis[x]小于平均值
你甚至可以和第一种优化联用,其实没那个必要
- 优先队列优化(堆优化)
使用小根堆实现,其思想其实和SLF很像,将权值小的点放在队首。以减少松弛操作的次数。
大佬都是手写堆,蒟蒻不会啊。我就介绍一下优先队列的实现。很简单的将队列变为优先队列就好了。当然时间复杂度我不敢保证,所以不是很推荐。但是手写堆很好的
核心代码
struct cs{
int x;
bool operator < (const cs &rhs) const {
return x > rhs.x;
}
}a;
priority_queue<cs>q;
或者
priority_queue<int,vector<int>,greater<int> >q;
2.判负环
我个人觉得这是该算法最有存在价值的地方。
但实际上Bellman-Ford在这上面表现的更出色
介绍两种判负环的方法
1. Bfs判负环
思想:在一个无负环的途中从起点到任意一个点最短距离经过的点最多只有 n 个
实现:
Frist
记录起点到当前节点的最短距离包含点的个数,定义一个cnt数组来记录,如果包含点的个数大于点的总数n,则存在负环
核心代码如下
if(dis[to]>dis[u]+e[i].val)
{
dis[to]=dis[u]+e[i].val;
cnt[to]=cnt[u]+1;
if(cnt[to]>n)
return false;
}
second
记录节点的入队次数,可定义一个in数组记录,如次数大于点的总数n, 则存在负环
核心代码
if(in[to]>=n)return false;
if(!vis[to])
{
q.push(to)
vis[to]=1;
in[to]++;
}
2. Dfs判负环
思想:刚刚松弛过的点来松弛其他的点,如果能够松弛当前点并且当前点还在栈中,那图中存在负环。
核心代码
if(dis[to]>dis[u]+e[i].val)
{
if(vis[j])
{
flag=false;
return;
}
}
3.其他算法
比如网络流找增广路之类的。
这里就不予与介绍了。