如何卡 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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM