SPFA算法的SLF優化 ——loj#10081. 「一本通 3.2 練習 7」道路和航線
阿新 • • 發佈:2018-08-17
。。 loj dijkstra 分享 spa 思想 text 超時 我見
今天做到一道最短路的題,原題https://loj.ac/problem/10081
題目大意為給一張有n個頂點的圖,點與點之間有m1條道路,m2條航線,道路是雙向的,且權值非負,而航線是單向的,權值可能為負,保證兩點之間如果有航線就不會有道路。現給定起始點s,求s到每個點的最短路徑,如果沒有則輸出“NO PATH”。
我當時看到這題那叫一個高興啊,以為又是一道水題,因為有負權邊,不能用Dijkstra,果斷用SPFA,那麽有沒有負環呢?經過實測數據並沒有負環,本以為可以輕松AC了,然而評測結果如下:
最後兩個測試點T了。手寫隊列,讀入優化,各種常數優化都用完了,還是超時。現在可以基本確定出數據的人賤賤地卡SPFA了。。。
怎麽辦呢,又不能用Dijkstra,這時我找到了某某任學長(聞角大仙)。
在某某任學長的幫助下,我了解了一下SPFA的SLF優化。簡單地說就是用雙端隊列來實現SPFA,當要把一個節點入隊時,判斷它與隊頭的大小,如果dis[v]<dis[h](v為帶入隊節點,t為隊頭),就把它從隊頭插入,否則從隊尾插入。有點貪心的思想,如果這個點比隊頭還要小的話,就先用它來松弛其它節點。部分代碼如下:
1 inline void SPFA(int s)
2 {
3 register int p,h,v,w;
4 fill(dis+1,dis+n+1,INF);
5 dis[s]=0 ;
6 vis[s]=true;
7 q.push_back(s);
8 do
9 {
10 h=q.front();q.pop_front();
11 vis[h]=false;
12 for(p=tail[h];p;p=e[p].last)
13 {
14 v=e[p].v;w=e[p].w;
15 if(dis[v]>dis[h]+w)
16 {
17 dis[v]=dis[h]+w;
18 if(!vis[v])
19 {
20 if(dis[v]<dis[q.front()])//這個判斷很重要,如果v點比隊頭更優,就把它從隊頭入隊
21 q.push_front(v);
22 else q.push_back(v);
23 vis[v]=true;
24 }
25 }
26 }
27 }while(!q.empty());
28 }
提交後測試情況:
快了好幾倍啊!
這裏我用的手寫的雙端隊列,因為比用STL更快,附上手寫代碼,這應該是我見過最優秀的手寫雙端隊列方式了:
struct Deque{
LL l, r, q[N];
Deque() {l = 0; r = 0;}
bool empty() {return !(l ^ r);}
void push_back(LL v) {q[r++] = v; r %= N;}
void push_front(LL v) {q[l = (l - 1 + N) % N] = v;}
void pop_front() {++l; l %= N;}
void pop_back() {r = (r - 1 + N) % N;}
LL front() {return q[l];}
};
此外SPFA還有LLL優化,這裏就不贅述了(其實是筆者不會),有興趣的朋友可以去了解一下。
2018-08-17
SPFA算法的SLF優化 ——loj#10081. 「一本通 3.2 練習 7」道路和航線