SPFA 的两种优化

SPFA 优化

众所周知,SPFA 它死了

但有些时候你会嫌支持负边的 dijkstra 麻烦,于是不得不选择 SPFA

那么,你需要 SPFA 优化!

SLF 优化

我们可以参考一下 dijkstra 的思路。

dijkstra 每次取队列中的最小值,减少了同一节点入队次数和更新 dis 次数,于是避免了死亡

那么我们感性理解一下,如果我们在 SPFA 中每次也取较小 dis 的点是不是也可以得到一定的优化呢?

我们可以通过双端队列,在节点入队时实现这一点:把需要入队的点的 dis 与队首的比较一下,如果小于队首则将节点从队首入队,否则从队尾入队。

LLL 优化

其实本质原理与 SLF 一样,只是实现方式不同啦 ~

同样使用双端队列,我们额外维护一下队列中所有点的 dis 的平均值,把即将入队的点与这个平均值比较,决定从队首还是队尾入队。

贴(伪)代码

显而易见,这两个优化并不相矛盾,所以我们可以同时使用!

int dis[N];
//起点是 S
inline void SPFA(int S)
{
    //下面这三个东西只在该函数中使用,于是把它们放入函数里,避免被外界调用,不易重名、出错
	static deque<int> q;//双端队列
	static bool inque[N];//记录每个点是否在队列中
	static int sum = 0;//因为平均数不方便维护,我们选择维护队列中 dis 的和
	//TODO: 此处应初始化 dis 数组为 inf
	dis[S] = 0;
	q.push_back(S);
	while (q.size()) {
		int x = q.front();
		q.pop_front();
		inque[x] = 0, sum -= dis[x];//出队维护qaq
		for (/*TODO: 遍历 x 的所有出边: 出边 e 和 对应点 y  */) {
			if (dis[y] > dis[x] + w[e]) {
				dis[y] = dis[x] + w[e];
				if (!inque[y]) {
//此处为两种优化的核心
                    //一定要注意队列为空的特殊情况
					if (q.empty() || dis[y] < dis[q.front()] || dis[y]*q.size() < sum)
						q.push_front(y);
					else q.push_back(y);
//
					inque[y] = 1, sum += dis[y];//入队记得维护qwq
				}
			}
		}
	}
}

十分简单好写呢,为什么不随手写一个呢

注意:LLL 有时可能因为常数问题导致负优化

上一篇:2021CCPC河南省赛(部分代码待更)


下一篇:CF1163F - Indecisive Taxi Fee 题解