SPFA的优化


【为什么要优化】

关于SPFA,他死了(懂的都懂)

 

进入正题。。。

 

一般来说,我们有三种优化方法。

 

SLF优化:

SLF优化,即 Small Label First  策略,使用 双端队列 进行优化。

一般可以优化15%~20%,在竞赛中比较常用。

设从 u 扩展出了 v ,队列中队首元素为 k ,若 dis[ v ] < dis[ k ] ,则将 v 插入队首,否则插入队尾。

注:队列为空时直接插入队尾。

 1 deque<int> q;  2 
 3 inline void spfa(int x)  4 {  5     memset(d,0x3f,sizeof(d));  6     memset(v,0,sizeof(v));  7     d[x]=0;v[x]=1;  8  q.push_back(x);  9     while(q.size()) 10  { 11         int index=q.front();q.pop_front(); 12         v[index]=0; 13         for(int i=head[index];i;i=g[i].next){ 14             int y=g[i].ver,z=g[i].edge; 15             if(d[y]>d[index]+z){ 16                 d[y]=d[index]+z; 17                 if(!v[y]){ 18                     if(!q.empty()&&d[y]>=d[q.front()]) q.push_back(y); 19                     else q.push_front(y); 20                     v[y]=1; 21  } 22  } 23  } 24  } 25 }

 

LLL优化:

LLL优化,即 Large Label Last  策略,使用 双端队列 进行优化。

一般用SLF+LLL可以优化50%左右,但是在竞赛中并不常用LLL优化。(所以我就懒得写了,这是从这个大佬那里嫖来的

设队首元素为 k ,每次松弛时进行判断,队列中所有 dis 值的平均值为 x 。

若 dist[ k ] > x ,则将 k 插入到队尾,查找下一元素,直到找到某一个 k 使得 dis[ k ] <= x ,则将 k 出队进行松弛操作。

 1 void spfa(){
 2     int u,v,num=0;
 3     long long x=0;
 4     list<int> q;
 5     for(int i=1;i<=n;i++){path[i]=MAX;vis[i]=false;}
 6     path[s]=0;
 7     vis[s]=true;
 8     q.push_back(s);
 9     num++;
10     while(!q.empty()){
11         u=q.front();
12         q.pop_front();
13         num--;x-=path[u];
14         while(num&&path[u]>x/num){
15             q.push_back(u);
16             u=q.front();
17             q.pop_front();
18         }
19         vis[u]=false;
20         for(int i=head[u];i;i=a[i].next){
21             v=a[i].to;
22             if(relax(u,v,a[i].w)&&!vis[v]){
23                 vis[v]=true;
24                 if(!q.empty()&&path[v]<path[q.front()])q.push_front(v);
25                 else q.push_back(v);
26                 num++;x+=path[v];
27             }
28         }
29     }
30     for(int i=1;i<=n;i++)printf("%d ",path[i]);
31     printf("\n");
32 }

 

DFS优化:

这种优化顾名思义,就是用dfs的思想代替bfs的思想来优化Bellman-Ford。

常常用于判断正/负环,时间复杂度可以达到O(m)(m是边)。思路是,我们每一次dfs的时候如果走回之前dfs过的点,那就是有环,除了这个dfs的标记,我们还可以打另一个vis数组记录更新过权值的节点,以后就不必重复更新,大大降低复杂度。

不过如果无环的话,那还是上面那两种优化稍微适用一点。代码比较短,但是不好扩展。

 1 inline bool spfa(int x)
 2 {
 3     dfs[x]=1;
 4     for(int i=head[x];i;i=g[i].next)
 5     {
 6         int y=g[i].ver,z=g[i].edge;
 7         if(!v[y]||d[y]<d[x]+z){
 8             if(dfs[y]) return 0;
 9             v[y]=1;
10             d[y]=d[x]+z;
11             if(!spfa(y)) return 0;
12         }
13     }
14     dfs[x]=0;
15     return 1;
16 }

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM