1. 程式人生 > >spfa及其優化

spfa及其優化

name 足夠 其中 truct pat 初始 scrip put ()

發現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及其優化