标签:兴趣 false 测试 有道 empty src 重要 get 今天
今天做到一道最短路的题,原题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」道路和航线
标签:兴趣 false 测试 有道 empty src 重要 get 今天
原文地址:https://www.cnblogs.com/gosick/p/9492634.html