spfa及其优化

Posted star_eternal

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spfa及其优化相关的知识,希望对你有一定的参考价值。

发现spfa居然也有优化,十分的震惊,现在由我细细道来(#^.^#)

Description
    给你一个有向且边权全部非负的图,输出1到n的最短路。
Input
    第一行两个自然数n(n<=100000)和m(m<=200000),表示点数和边数。接下来m行,每行3个数a,b,l,其中1<=a,b<=n,l<=1000。
Output
    仅一个整数,为1到n的最短路。如果无解,输出-1。
Sample Input
10 10
1 5 46
1 10 50
2 7 23
3 4 40
3 7 3
3 9 21
4 9 27
5 8 45
5 10 30
7 10 15
Sample Output
50
Hint
 如果是练SPFA,过90分就可以了(不过如果你的SPFA可以过完,在下拜你为师……)。

这个出题人要拜我为师o(* ̄︶ ̄*)o

我们会想一下bell——ford如何进化到spfa.

SPFA对Bellman-Ford算法优化的关键之处在于意识到:只有那些在前一遍松弛中改变了距离估计值的点,才可能引起他们的邻接点的距离估计值的改变。因此,算法大致流程是用一个队列来进行维护,即用一个先进先出的队列来存放被成功松弛的顶点。初始时,源点s入队。当队列不为空时,取出队首顶点, 对它的邻接点进行松弛。如果某个邻接点松弛成功,且该邻接点不在队列中,则将其入队。经过有限次的松弛操作后,队列将为空,算法结束。SPFA算法的实现,需要用到一个先进先出的队列queue 和一个指示顶点是否在队列中的标记数组mark。为了方便查找某个顶点的邻接点,图采用邻接表存储。

这样,我们的加入队列的顺序是随机的,现在,让我们看看两个优化。

一个叫做SLF,一个叫做LLL,先%一下

SLF又称Small Label First 策略. (比较常用)

比较当前点和队首元素,如果小于队首,则插入队首,否则加入队尾

这个优化有什么道理呢,我想是这样的

1.每次加入了边后,都要对可以到达的点松弛,如果先加入队列的元素足够小,那么他的更新效果更加彻底,下一次碰到与这个点相连的点,这个点就很大概率不会入队了,这样就可以减少运算了

嘿嘿(#^.^#)

而LLL又称LLL: Large Label Last 策略. (不太常用)

设队首元素为i,每次弹出时进行判断,队列中所有dist值的平均值为x,若dist(i)>x则将i插入到队尾,查找下一元素,直到找到某一i使得dist(i)<=x,则将i出对进行松弛操作。

据说一些数据卡LLL可以卡到指数级只不过LLL还是挺好的(以后还是LLL少用)

发一个SLF优化的代码

#include<bits/stdc++.h>

using namespace  std;

const int maxn=100000+10;

const int INF=0x7FFFFFFF;

int pre[maxn];

int dis[maxn];

int path[maxn];

bool vis[maxn];

int head[maxn];
int n,m;

int tot,cnt;
struct node
{
    int v,w,next;
}E[2*maxn];
void add(int u,int v,int w)
{

    E[tot].v=v;

    E[tot].w=w;

    E[tot].next=head[u];

    head[u]=tot++;

}
 void init()

{

    tot=0;

    memset(vis,false,sizeof((vis)));

    memset(head,-1,sizeof(head));

}

void spfa(int st)

{

    for(int i=1;i<=n;i++)
        vis[i]=false,dis[i]=INF;
    int now,next;
    dis[st]=0;
    vis[st]=true;
    deque<int>q;
    q.push_back(st);
    pre[st]=-1;
    while(!q.empty())
    {
        now=q.front();
        q.pop_front();
        vis[now]=false;
        for(int i=head[now];i!=-1;i=E[i].next)
        {
            next=E[i].v;
            if(dis[next]>dis[now]+E[i].w)
            {
                dis[next]=dis[now]+E[i].w;
                pre[next]=now;
                 if(!vis[next])
                 {
                      vis[next]=true ; 
                     if( q.empty()||dis[next]>dis[q.front()]) q.push_back(next);
                         else q.push_front(next);
                }
            }

        }

    }

}
void print(int x)
{
    if(pre[x]==-1) return;
    print(pre[x]);
    printf("%d ",x);
}
int main()

{
        init();
        scanf("%d%d",&n,&m);
              // int st,en;
//        scanf("%d%d",&st,&en);
        int u,v,w;
        for(int i=1;i<=m;i++){scanf("%d%d%d",&u,&v,&w);add(u,v,w);}
        spfa(1);
        if(dis[n]==INF)
        {printf("-1");return 0;}
        printf("%d\n",dis[n]);
        //printf("%d ",st);
        //print(en);

        printf("\n");

    
    return 0;
}

 

以上是关于spfa及其优化的主要内容,如果未能解决你的问题,请参考以下文章

常用最短路优化算法及例题(附模板)——-SPFA和Dijkstra

SPFA的两种优化

Spfa + SLF&LLL优化

SPFA的两个优化:SLF与LLL

spfa有堆优化吗

算法描述》关于SPFA和Dijkstra算法的两三事