SPFA最短路

來源:互聯網
上載者:User

B-F 

適用條件&範圍

  1. 單源最短路徑(從源點s到其它所有頂點v);
  2. 有向圖&無向圖(無向圖可以看作(u,v),(v,u)同屬於邊集E的有向圖);
  3. 邊權可正可負(如有負權迴路輸出錯誤提示);
  4. 差分約束系統;

演算法描述
  1. 對每條邊進行|V|-1次Relax ( 就是鬆弛操作 )操作;
  2. 如果存在(u,v)∈E使得dis[u]+w<dis[v],則存在負權迴路;否則dis[v]即為s到v的最短距離,pre[v]為前驅。
For i:=1 to |V|-1 do //v為頂點數For 每條邊(u,v)∈E do  //對每條邊進行遍曆  Relax(u,v,w);For每條邊(u,v)∈E do  If dis[u]+w<dis[v] Then Exit(False)

時空複雜度

演算法時間複雜度O(VE)。因為演算法簡單,適用範圍又廣,雖然複雜度稍高,仍不失為一個很實用的演算法。

 

演算法的改進---> SPFA 複雜度O(KE),k ≈ 2

 

演算法簡介

SPFA(Shortest Path Faster Algorithm)是Bellman-Ford演算法的一種隊列實現,減少了不必要的冗餘計算。也有人說SPFA本來就是Bellman-Ford演算法,現在廣為流傳的Bellman-Ford演算法實際上是山寨版。

演算法流程

演算法大致流程是用一個隊列來進行維護。 初始時將源排入佇列。 每次從隊列中取出一個元素,並對所有與他相鄰的點進行鬆弛(見下文最後),若某個相鄰的點鬆弛成功,則將其入隊。
直到隊列為空白時演算法結束。

這個演算法,簡單的說就是隊列最佳化的bellman-ford,利用了每個點不會更新次數太多的特點發明的此演算法

SPFA——Shortest Path Faster Algorithm,它可以在O(kE)的時間複雜度內求出源點到其他所有點的最短路徑,可以處理負邊。SPFA的實現甚至比Dijkstra或者Bellman_Ford還要簡單:

設Dist代表S到I點的當前最短距離,Fa代表S到I的當前最短路徑中I點之前的一個點的編號。開始時Dist全部為+∞,只有Dist[S]=0,Fa全部為0。

維護一個隊列,裡面存放所有需要進行迭代的點。初始時隊列中只有一個點S。用一個布爾數組記錄每個點是否處在隊列中。

每次迭代,取出隊頭的點v,依次枚舉從v出發的邊v->u,設邊的長度為len,判斷Dist[v]+len是否小於Dist[u],若小於則改進Dist[u],將Fa[u]記為v,並且由於S到u的最短距離變小了,有可能u可以改進其它的點,所以若u不在隊列中,就將它放入隊尾。這樣一直迭代下去直到隊列變空,也就是S到所有的最短距離都確定下來,結束演算法。若一個點入隊次數>=n,則有負權環。

SPFA 在形式上和寬度優先搜尋非常類似,不同的是寬度優先搜尋中一個點出了隊列就不可能重新進入隊列,但是SPFA中一個點可能在出隊列之後再次被放入隊列,也就是一個點改進過其它的點之後,過了一段時間可能本身被改進,於是再次用來改進其它的點,這樣反覆迭代下去。設一個點用來作為迭代點對其它點進行改進的平均次數為k,有辦法證明對於通常的情況,k在2左右

 

數組實現鄰接表模板

#define N 100004const int INF = (1<<30);int n,m;//n是最後一個點的編號struct edge{    int u,v,w;    int next;//同一起點的下一條邊儲存在edge數組中的位置(理解了這個靜態鄰接表就可以了)}e[N*10];int head[N];//以該點為起點的第一條邊儲存在e數組中的位置int dis[N];//記錄與源點距離bool vis[N];//記錄頂點是否在隊列中,SPFA演算法可以入隊列多次int cnt[N];//記錄頂點入隊列次數int ecnt;void init(){    ecnt = 0;    memset(head,-1,sizeof(head));}void add(int u,int v,int w){    e[ecnt].u = u;    e[ecnt].v = v;    e[ecnt].w = w;    e[ecnt].next = head[u];    head[u] = ecnt++;//位置更新}bool SPFA(int s){//s是源點編號    queue<int>  qq;    int i;    for(i=1;i<=n;++i){        dis[i] = INF;        //將除源點以外的其餘點的距離設定為無窮大        vis[i] = 0;        cnt[i] = 0;    }    dis[s]=0;              //源點的距離為0    vis[s] = 1;    cnt[s]++;            //源點的入隊列次數增加    qq.push(s);    int u,v;    while(!qq.empty()){        u = qq.front();        qq.pop();        vis[u] = 0;        for(i=head[u];i!=-1;i = e[i].next){            v = e[i].v;            int cost = e[i].w;            if(dis[v] > cost+dis[u]){                dis[v] = cost+dis[u];                if(!vis[v]){                    vis[v] = 1;                    qq.push(v);                    cnt[v]++;                    if(cnt[v] >= n)return false;                }            }        }    }    return true;}

vector實現鄰接表模板

#define N 50001 const int INF = 0x7fffffff;int n,m;typedef struct edge{       int to;       int w;}edge,temp;vector<edge> adjmap[N]; //vector實現鄰接表int d[N];bool vis[N];          //記錄頂點是否在隊列中,SPFA演算法可以入隊列多次int cnt[N];             //記錄頂點入隊列次數void SPFA(){         queue<int>     myqueue;         int i;         for(i=2;i<=n;++i)                    d[i] = INF;        //將除源點以外的其餘點的距離設定為無窮大         memset(vis,0,sizeof(vis));         memset(cnt,0,sizeof(cnt));         d[1]=0;              //源點的距離為0         vis[1] = true;         cnt[1]++;            //源點的入隊列次數增加         myqueue.push(1);         int topint;         while(!myqueue.empty())         {                 topint = myqueue.front();                 myqueue.pop();                 vis[topint] = false;                 for(i=0;i<adjmap[topint].size();++i)                 {                         int to = adjmap[topint][i].to;                         if(d[topint]<INF && d[to]>d[topint]+ adjmap[topint][i].w)                         {                                  d[to] = d[topint]+ adjmap[topint][i].w;                                  if(!vis[to])                                  {                                          vis[to] = true;                                          cnt[to]++;                                          if(cnt[to]>=n)   //當一個點入隊的次數>=n時就證明出現了負環。                                            return ;                                          myqueue.push(to);                                  }                     }              }                   } printf("%d/n",d[n]);}int main(){        freopen("a.txt","r",stdin);        scanf("%d%d",&n,&m);        int i;        int s,e,w;        edge temp;        for(i=1;i<n+1;++i)       //此處特別注意對鄰接表清空            adjmap[i].clear();        for(i=0;i<m;++i)         //雙向        {                cin>>s>>e>>w;                temp.to = e;                temp.w = w;                adjmap[s].push_back(temp);                temp.to = s;                adjmap[e].push_back(temp);        }         SPFA();         return 0;}


數組版

int map[N][N],dist[N],vis[N];void SPFA(int s){    int i,j;    for(i=0;i<=n;i++){        dist[i] = maxsum;        vis[i] = 0;    }    queue<int> q;    dist[s]=0;    q.push(s);    vis[s]=1;    while(!q.empty()){        int x=q.front();        q.pop();        vis[x]=0;        for(i=1;i<=n;i++){            if(map[x][i] != INF && dist[x]+map[x][i]<dist[i]){                dist[i]=dist[x]+map[x][i];                if(!vis[i]){                    vis[i]=1;                    q.push(i);                }            }        }    }}


如果要記錄最短路徑的話就需要開多一個數組pre[i],若且唯若dis[i]+map[i][j]<dis[j]時更新pre[j] = i

 

 

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.