如何卡 spfa

前言

模拟赛中最短路有人写了 spfa 。

但是出题人居然没卡,于是我去网上学了下造了一组数据卡死了。

但是看了这个之后觉得很有意思,于是打算写一个博客来记录一下一些卡 spfa 的例子。

正文

首先就是传统的 spfa 怎么卡。

下面是一个 spfa 的示例代码。

void spfa() {
  for(int i = 1; i <= n; i++) dis[i] = INF, vis[i] = 0;
  queue<int>q;
  dis[0] = 0;
  q.push(0);
  while(!q.empty()) {
    int u = q.front(); q.pop();
    vis[u] = 0;
    for(int i = first[u]; i; i = nex[i]) {
      int to = v[i];
      int val = w[i];
      if(dis[to] > dis[u] + val) {
        dis[to] = dis[u] + val;
        if(!vis[to]){
          vis[to] = 1;
          q.push(to);
        }
      }
    }
  }
}

首先我们要清楚卡 spfa 的原理是什么,卡 spfa 其实是让点尽可能多次的入队,然后反复更新。

根据代码我们知道,如果一个点,被多个边连着,那么当这些,然后当这些边先依次走向他的时候,那么这个点就会被调用很多次,然后往下走,于是我们可以去构造一个链套菊花,这样的话,对于菊花的那个点,会反复入队列很多次,然后往下每次都要走很多次,于是就死了。

另一种卡法是构造成为一个网格图,然后边进行随机排列。因为如果在网格图中走错了一次,就会走很多步本不需要的步数,于是复杂度就高了。

那么结合两种卡法,对于普通的 spfa 最好的卡法就是将图构造为一个网格套链套菊花。

这样就是死的不能再死了。

构造很简单,就是考虑先提出一个点,然后作为终点然后先构建网格图,然后对于终点进行很多的连边,然后以终点再往下构造网格图,然后这个网格图里面又要求尽量是很多链,实现有点复杂,不过一般情况下估计上面两种就是能卡死了。

然后对于网格图也是有讲究的。

我们的网格图的行要比列小很多,这样的话,我们的图才更加的稠密,然后走错后的代价更大。

然后竖着的边边权我们设越小越好,横着的越大越好。

这样下去他的更新次数就会更多,然后继续下去的概率就会更大。

如果每次将进队最多的点放到了队首可能会通过这个网格图,但是我们采用网格套菊花啥的仍然可以卡死。

然后是对于一些奇怪的优化的卡法。

\(\text{LLL}\) 优化:每次将入队结点距离和队内距离平均值比较,如果更大则插入至队尾。

\(\text{Hack}\):向 \(1\) 连接一条权值巨大的边,然后其余正常的构造就好了。

\(\text{SLF}\) 优化:每次将入队结点距离和队首比较,如果更大则插入至队尾。

\(\text{Hack}\):使用链套菊花的方法,在链上用几个并列在一起的小边权边就能欺骗算法多次进入菊花。反正很强,第一次知道。

\(\text{SLF 带容错}\) : 每次将入队结点距离和队首比较,如果比队首大超过一定值则插入至队尾。

\(\text{Hack}:\) 仍然是上面的卡法,然后不是让距离大就插进队尾吗,插进队尾这样的话有可能被更新过一些次数后,就不能继续往下走了,那么你把边权赋值都开大,然后让最后出来的图的距离尽量还差距小,那么就卡死了。

\(\text{mcfx 优化}\) : 当一个点被第 \([L,R]\) 次访问到的时候,将其放入队尾,否则放入队首。

\(\text{Hack}\) : 链套菊花图,具体怎么证的不会。

于是考虑大多数的卡法直接就用普通的卡法然后加上我们的那些特殊边权赋值等,就可以了。

然后是一个额外的卡法,这个来自于猫老师(原文复制):

\(\text{Step 1}\) 生成一棵以起点为根的树,树高尽量高(比如 \(1\) 为起点时,可以令每个点 \(i\) 的父亲在 \(\max(i-5,1)\)\(i-1\) 随机),边权随机,作为最短路径树,同时直接递推求出每个点的带权深度 \(d(i)\)

\(\text{Step 2}\) 对于剩下的边,端点 \((a, b)\) 随机,边权在 \(|d(b)-d(a)|\)\(|d(b)-d(a)|+5\) 随机(如果是有向图则去掉绝对值符号,\(5\) 可以换成其他较小的正数)这样生成的图中,次短路的条数非常的多,而 SPFA 一旦错误地进入了次短路的分支,就会使得一整棵子树被赋错误的距离,从而在后期不得不重新更新。而由于边权接近,剪枝的效果会受到很大影响。

然后这个方法只用随机就行了,膜拜猫老师!

然后我很赞同猫老师的观点,个人认为,在非负边权的图中,不卡 spfa 是没有责任心的,如果是一个好的出题人,spfa 是一定要卡掉的。

参考资料:
[1] https://www.zhihu.com/question/292283275/answer/484871888 中猫老师和 fstqwq 的回答

[2] https://blog.csdn.net/yfzcsc/article/details/77623365

[3] 一些大佬的指教

[4] https://blog.csdn.net/houserabbit/article/details/38306447

posted @ 2021-11-01 18:06  Pitiless0514  阅读(2605)  评论(10编辑  收藏  举报