以爲一篇講SPFA還不錯的文章

我以爲他整理的有一些亂,我都改爲插入代碼了,看的順眼一些

轉載自http://blog.csdn.net/juststeps/article/details/8772755

下面的都是原文:node

 

 

最短路徑 之 SPFA算法

http://hi.baidu.com/southhill/item/ab26a342590a5aae60d7b967ios

求最短路徑的算法有許多種,除了排序外,恐怕是OI界中解決同一類問題算法最多的了。最熟悉的無疑是Dijkstra,接着是Bellman-Ford,它們均可以求出由一個源點向其餘各點的最短路徑;若是咱們想要求出每一對頂點之間的最短路徑的話,還能夠用Floyd-Warshall。算法

SPFA是這篇日誌要寫的一種算法,它的性能很是好,代碼實現也並不複雜。特別是當圖的規模大,用鄰接矩陣存不下的時候,用SPFA則能夠很方便地面對臨接表。每一個人都寫過廣搜,SPFA的實現和廣搜很是類似。數組

如何求得最短路徑的長度值?ide

首先說明,SPFA是一種單源最短路徑算法,因此如下所說的「某點的最短路徑長度」,指的是「某點到源點的最短路徑長度」。函數

咱們記源點爲S,由源點到達點i的「當前最短路徑」爲D[i],開始時將全部D[i]初始化爲無窮大,D[S]則初始化爲0。算法所要作的,就是在運行過程當中,不斷嘗試減少D[]數組的元素,最終將其中每個元素減少到實際的最短路徑。性能

過程當中,咱們要維護一個隊列,開始時將源點置於隊首,而後反覆進行這樣的操做,直到隊列爲空:優化

(1)從隊首取出一個結點u,掃描全部由u結點能夠一步到達的結點,具體的掃描過程,隨存儲方式的不一樣而不一樣;spa

(2)一旦發現有這樣一個結點,記爲v,知足D[v] > D[u] + w(u, v),則將D[v]的值減少,減少到和D[u] + w(u, v)相等。其中,w(u, v)爲圖中的邊u-v的長度,因爲u-v必相鄰,因此這個長度必定已知(否則咱們獲得的也不叫一個完整的圖);這種操做叫作鬆弛。.net

 

引用內容
鬆弛操做的原理是著名的定理:「三角形兩邊之和大於第三邊」,在信息學中咱們叫它三角不等式。所謂對i,j進行鬆弛,就是斷定是否d[j]>d[i]+w[i,j],若是該式成立則將d[j]減少到d[i]+w[i,j],不然不動。

(3)上一步中,咱們認爲咱們「改進了」結點v的最短路徑,結點v的當前路徑長度D[v]相比於之前減少了一些,因而,與v相連的一些結點的路徑長度可能會相應地減少。注意,是可能,而不是必定。但即便如此,咱們仍然要將v加入到隊列中等待處理,以保證這些結點的路徑值在算法結束時被降至最優。固然,若是鏈接至v的邊較多,算法運行中,結點v的路徑長度可能會屢次被改進,若是咱們所以而將v加入隊列屢次,後續的工做無疑是冗餘的。這樣,就須要咱們維護一個bool數組Inqueue[],來記錄每個結點是否已經在隊列中。咱們僅將還沒有加入隊列的點加入隊列。


算法可否結束?

對於不存在負權迴路的圖來講,上述算法是必定會結束的。由於算法在反覆優化各個最短路徑長度,總有一個時刻會進入「沒法再優化」的局面,此時一旦隊列讀空,算法就結束了。然而,若是圖中存在一條權值爲負的迴路,就糟糕了,算法會在其上反覆運行,經過「繞圈」來無休止地試圖減少某些相關點的最短路徑值。假如咱們不能保證圖中沒有負權迴路,一種「結束條件」是必要的。這種結束條件是什麼呢?

思考Bellman-Ford算法,它是如何結束的?顯然,最樸素的Bellman-Ford算法無論循環過程當中發生了什麼,一律要循環|V|-1遍才肯結束。憑直覺咱們能夠感到,SPFA算法「更聰明一些」,就是說咱們能夠猜想,假如在SPFA中,一個點進入隊列——或者說一個點被處理——超過了|V|次,那麼就能夠判定圖中存在負權迴路了。


最短路徑自己怎麼輸出?

在一幅圖中,咱們僅僅知道結點A到結點E的最短路徑長度是73,有時候意義不大。這附圖若是是地圖的模型的話,在算出最短路徑長度後,咱們總要說明「怎麼走」纔算真正解決了問題。如何在計算過程當中記錄下來最短路徑是怎麼走的,並在最後將它輸出呢?

Path[]數組,Path[i]表示從S到i的最短路徑中,結點i以前的結點的編號。注意,是「以前」,不是「以後」。最短路徑算法的核心思想成爲「鬆弛」,原理是三角形不等式,方法是上文已經說起的。咱們只須要在藉助結點u對結點v進行鬆弛的同時,標記下Path[v] = u,記錄的工做就完成了。

輸出時可能會遇到一點難處,咱們記的是每一個點「前面的」點是什麼,輸出卻要從最前面往最後面輸,這很差辦。其實很好辦,見以下遞歸方法:

 

程序代碼
void PrintPath(int k){
    if( Path[k] ) PrintPath(Path[k]);
    fout<<k<<' ';
}


SPFA的代碼怎麼寫?

我寫了鄰接表和鄰接矩陣兩種,二者想像起來是那麼的不一樣,算法的思路上實在區別不大,只是用不一樣方式詮釋「掃描」的過程而已。只給出SPFA的單個函數,我不以爲很容易看懂,可是我仍然把兩個程序的SPFA函數放在下面。在日誌的結尾處,有一個完整版文件下載。貼程序,首先是鄰接表的:

 

程序代碼

void SPFA(){
    for(int i=1; i<=gv; i++)
        Dist[i] = 100000;
    Dist[S] = 0;
    int closed = 0, open = 1;
    queue[1] = S;
    Inqueue[S] = true;
    do{
        closed++;
        node *tmp = connect[queue[closed]];
        Inqueue[queue[closed]] = false;
        while(tmp != NULL){
            if( Dist[tmp->key] > Dist[queue[closed]] + tmp->w ){
                Dist[tmp->key] = Dist[queue[closed]] + tmp->w;
                Path[tmp->key] = queue[closed];
                if( !Inqueue[tmp->key] ){
                    Inqueue[tmp->key] = true;
                    open++;
                    queue[open] = tmp->key;
                }
            }
            tmp = tmp->next;
        }
    }while(closed < open);
}

 

而後是鄰接矩陣的:

 

程序代碼

void SPFA(){
    for( int i=1; i<=gv; i++){
        Dist[i] = 100000;
        for( int j=1; j<=gv; j++)
            if( !Graph[i][j] && i!=j) Graph[i][j] = 100000;
    }
    int closed = 0, open = 1;
    queue[1] = S;
    Dist[S] = 0;
    do{
        closed++;
        int u = queue[closed];
        Inqueue[u] = false;
        for(int i=1; i<=gv; i++)
            if ( Dist[i] > Dist[u] + Graph[u][i] ){
                Dist[i] = Dist[u] + Graph[u][i];
                Path[i] = u;
                if( !Inqueue[i] ){
                    Inqueue[i] = true;
                    open++;
                    queue[open] = i;
                }
            }
    }while(closed < open);
}

 


spfa算法 Easy sssp 收藏 
輸入數據給出一個有N(2 <= N <= 1,000)個節點,M(M <= 100,000)條邊的帶權有向圖. 
要求你寫一個程序, 判斷這個有向圖中是否存在負權迴路. 若是從一個點沿着某條路徑出發, 又回到了本身, 並且所通過的邊上的權和小於0, 就說這條路是一個負權迴路.
若是存在負權迴路, 只輸出一行-1;
若是不存在負權迴路, 再求出一個點S(1 <= S <= N)到每一個點的最短路的長度. 約定: S到S的距離爲0, 若是S與這個點不連通, 則輸出NoPath.

INPUT:
第一行: 點數N(2 <= N <= 1,000), 邊數M(M <= 100,000), 源點S(1 <= S <= N);
如下M行, 每行三個整數a, b, c表示點a, b(1 <= a, b <= N)之間連有一條邊, 權值爲c(-1,000,000 <= c <= 1,000,000)

OUTPUT:
若是存在負權環, 只輸出一行-1, 不然按如下格式輸出
共N行, 第i行描述S點到點i的最短路: 
若是S與i不連通, 輸出NoPath;
若是i = S, 輸出0;
其餘狀況輸出S到i的最短路的長度

INPUT:
6 8 1
1 3 4
1 2 6
3 4 -7
6 4 2
2 4 5
3 6 3
4 5 1
3 5 4

OUTPUT:
0
6
4
-3
-2
7

注意:
題目說的不是很清楚,給出的圖不必定是徹底聯通圖,有些是斷開的幾個圖,因此在判斷的源點是否有環之外還要分別對不一樣的點進行spfa呀。再進行分別的判斷和輸出。

有幾個優化:
1.能夠先判斷是否有負權自環,有則直接輸出-1
2.在枚舉的過程當中,當這個頂點的最短路(d[i])<0時,有負權迴路,輸出-1.

【參考程序】:

  1 #include<stdio.h>
  2 #include<string.h>
  3 #include<stdlib.h>
  4 long queue[1001],a[1001],psum[1001],dis[1001],l[1001][1001],cost[1001][1001];
  5 long n,m,s,i,j;
  6 bool hash[1001],bk;
  7 void spfa(int s)
  8 {
  9     int head,tail,start,now,i;
 10     for (i=1;i<=n;i++)
 11     {
 12         dis[i]=0xfffffff;
 13         psum[i]=0;
 14         hash[i]=false;
 15     }
 16     head=tail=1;hash[s]=true;
 17     psum[s]=1;dis[s]=0;queue[1]=s;
 18     while (head<=tail)
 19     {
 20         start=queue[(head-1)%n+1];
 21         hash[start]=true;
 22         for (i=1;i<=l[start][0];i++)
 23         {
 24             now=l[start][i];
 25             if (dis[now]>dis[start]+cost[start][now])
 26             {
 27                 dis[now]=dis[start]+cost[start][now];
 28                 if (!hash[now])
 29                 {
 30                     hash[now]=true;
 31                     tail++;
 32                     queue[(tail-1)%n+1]=now;
 33                     psum[now]++;
 34                     if (psum[now]>n)
 35                     {//記錄每一個點進隊的次數(判斷環的關鍵}
 36                         bk=false;
 37                         return;
 38                     }
 39                 }
 40             }
 41         }
 42         head++;
 43         hash[start]=false;
 44         if (dis[s]<0)
 45         {//判斷環的一個優化
 46             bk=false;
 47             return;
 48         }
 49     }
 50 }
 51 void output()
 52 {
 53     bk=true;
 54     spfa(s);
 55     if (!bk)
 56     {
 57         printf("-1\n");
 58         return;
 59     }
 60     memcpy(a,dis,sizeof(long)*(n+1));
 61     for (i=1;i<=n;i++)
 62       if (a[i]==0xfffffff)
 63       {
 64             bk=true;
 65             spfa(i);
 66             if (!bk)
 67             {
 68                 printf("-1\n");
 69                 return;
 70             }
 71       }
 72     for (i=1;i<=n;i++)
 73       if (a[i]==0xfffffff) printf("NoPath\n");
 74       else printf("%d\n",a[i]);
 75 }
 76 void input()
 77 {
 78     scanf("%d%d%d",&n,&m,&s);
 79     for (i=1;i<=n;i++)
 80       for (j=1;j<=n;j++)
 81         if (i==j) cost[i][j]=0;
 82         else cost[i][j]=0xfffffff;
 83     memset(l,0,sizeof(l));
 84     int x,y,c;
 85     for (i=1;i<=m;i++)
 86     {
 87         scanf("%d%d%d",&x,&y,&c);
 88         if (c<cost[x][y])
 89         {
 90             cost[x][y]=c;
 91             l[x][0]++;
 92             l[x][l[x][0]]=y;
 93         }
 94     }
 95 }
 96 int main()
 97 {
 98     input();
 99     output();
100     system("pause");
101     return 0;
102 }
View Code

 

 

本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/bobcowwocb/archive/2009/09/14/4550188.aspx

2009年07月24日 星期五 15:10
SPFA算法模版+鄰接表實現 
SPFA即shotest path faster algorithm,由意思就能夠看出該算法效率比較高。

其實SPFA就是bellman-ford算法的一個優化。

具體作法是用一個隊列保存待鬆弛的點,而後對於每一個出隊的點依次遍歷每一個與他有邊相鄰的點(用鄰接表效率較高),若是該點能夠鬆弛而且隊列中沒有該點則將它加入隊列中,如此迭代直到隊列爲空。

聽說平均效率是O(E),可見對邊稀疏的圖用此算法效果是至關可觀的。

 

若要判負環路,則記錄一個點的入隊次數,若超過邊數,則有負權環。

 

  1 #include <iostream>
  2 #include <queue>
  3 using namespace std;
  4 
  5 const long MAXN=10000;
  6 const long lmax=0x7FFFFFFF;
  7 
  8 typedef struct 
  9 {
 10     long v;
 11     long next;
 12     long cost;
 13 }Edge;
 14 
 15 
 16 Edge e[MAXN];
 17 long p[MAXN];
 18 long Dis[MAXN];
 19 bool vist[MAXN];
 20 
 21 queue<long> q;
 22 
 23 long m,n;//點,邊
 24 void init()
 25 {
 26     long i;
 27     long eid=0;
 28 
 29     memset(vist,0,sizeof(vist));
 30     memset(p,-1,sizeof(p));
 31     fill(Dis,Dis+MAXN,lmax);
 32 
 33     while (!q.empty())
 34     {
 35         q.pop();
 36     }
 37 
 38     for (i=0;i<n;++i)
 39     {
 40         long from,to,cost;
 41         scanf("%ld %ld %ld",&from,&to,&cost);
 42 
 43         e[eid].next=p[from];
 44         e[eid].v=to;
 45         e[eid].cost=cost;
 46         p[from]=eid++;
 47 
 48         //如下適用於無向圖
 49         swap(from,to);
 50         
 51         e[eid].next=p[from];
 52         e[eid].v=to;
 53         e[eid].cost=cost;
 54         p[from]=eid++;
 55 
 56     }
 57 }
 58 
 59 void print(long End)
 60 {
 61     //若爲lmax 則不可達
 62     printf("%ld\n",Dis[End]);    
 63 }
 64 
 65 void SPF()
 66 {
 67 
 68     init();
 69 
 70     long Start,End;
 71     scanf("%ld %ld",&Start,&End);
 72     Dis[Start]=0;
 73     vist[Start]=true;
 74     q.push(Start);
 75 
 76     while (!q.empty())
 77     {
 78         long t=q.front();
 79         q.pop();
 80         vist[t]=false;
 81         long j;
 82         for (j=p[t];j!=-1;j=e[j].next)
 83         {
 84             long w=e[j].cost;
 85             if (w+Dis[t]<Dis[e[j].v])
 86             {
 87                 Dis[e[j].v]=w+Dis[t];
 88                 if (!vist[e[j].v])
 89                 {
 90                     vist[e[j].v]=true;
 91                     q.push(e[j].v);
 92                 }
 93             }
 94         }
 95     }
 96 
 97     print(End);
 98 
 99 }
100 
101 int main()
102 {
103     while (scanf("%ld %ld",&m,&n)!=EOF)
104     {
105         SPF();
106     }
107     return 0;
108 }
View Code

 

1、Bellman-Ford算法

最優性原理


它是最優性原理的直接應用,算法基於如下事實:

l          若是最短路存在,則每一個頂點最多通過一次,所以不超過n-1條邊;

l          長度爲k的路由長度爲k-1的路加一條邊獲得;

l          由最優性原理,只需依次考慮長度爲1,2,…,k-1的最短路。

適用條件&範圍

l          單源最短路徑(從源點s到其它全部頂點v);

l          有向圖&無向圖(無向圖能夠看做(u,v),(v,u)同屬於邊集E的有向圖);

l          邊權可正可負(若有負權迴路輸出錯誤提示);

l          差分約束系統(須要首先構造約束圖,構造不等式時>=表示求最小值, 做爲最長路,<=表示求最大值, 做爲最短路。<=構圖時, 有負環說明無解;求不出最短路(爲Inf)爲任意解。>=構圖時相似)。

算法描述

l          對每條邊進行|V|-1次Relax操做;

l          若是存在(u,v)∈E使得dis[u]+w<dis[v],則存在負權迴路;不然dis[v]即爲s到v的最短距離,pre[v]爲前驅。

時空複雜度                                                                                           

for i:=1 to |V|-1 do

    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)。由於算法簡單,適用範圍又廣,雖然複雜度稍高,仍不失爲一個很實用的算法。

改進和優化   若是循環n-1次之前已經發現不存在緊邊則能夠當即終止; Yen氏改進(不下降漸進複雜度);SPFA算法

2、             SPFA算法

算法簡介 
SPFA(Shortest Path Faster Algorithm)是Bellman-Ford算法的一種隊列實現,減小了沒必要要的冗餘計算。 它能夠在O(kE)的時間複雜度內求出源點到其餘全部點的最短路徑,能夠處理負邊。

算法流程 
SPFA對Bellman-Ford算法優化的關鍵之處在於意識到:只有那些在前一遍鬆弛中改變了距離估計值的點,纔可能引發他們的鄰接點的距離估計值的 改變。所以,算法大體流程是用一個隊列來進行維護,即用一個先進先出的隊列來存放被成功鬆弛的頂點。初始時,源點s入隊。當隊列不爲空時,取出隊首頂點, 對它的鄰接點進行鬆弛。若是某個鄰接點鬆弛成功,且該鄰接點不在隊列中,則將其入隊。通過有限次的鬆弛操做後,隊列將爲空,算法結束。SPFA算法的實 現,須要用到一個先進先出的隊列 queue 和一個指示頂點是否在隊列中的標記數組mark。爲了方便查找某個頂點的鄰接點,圖採用臨界表存儲。

算法代碼 
Procedure SPFA;Begin             initialize-single-source(G,s);             initialize-queue(Q);             enqueue(Q,s);             while not empty(Q) do begin                u:=dequeue(Q);                for each v∈adj[u] do begin                   tmp:=d[v]; relax(u,v);                   if (tmp<>d[v]) and (not v in Q) then enqueue(v);                   end;                end;End;負環處理
   須要特別注意的是:僅當圖不存在負權迴路時,SPFA能正常工做。若是圖存在負權迴路,因爲負權迴路上的頂點沒法收斂,總有頂點在入隊和出隊往返,隊列沒法爲空,這種狀況下SPFA沒法正常結束。

判斷負權迴路的方案不少,世間流傳最廣、比較容易實現而且高效的方法的是記錄每一個結點進隊次數,超過|V|次表示有負權。

3、             學以至用

POJ 1201 Intervals 差分約束系統

設S(i)爲 0..i-1 中在最終序列中的的整數個數。則約束條件以下:

S(b)-S(a) >= c

0 <= S(i+1) - S(i) <= 1 <==> S(i+1)-S(i) >= 0;

                             S(i)-S(i+1) >= -1

注意本題要求的是最小值, 而按照>=號建圖後發現圖中有負環, 怎麼辦呢?

其實很簡單, 本題求的不是最短路, 而是最長路! Bellman_ford便可!

POJ 1275 Cashier Employment 出納員的僱傭

黑書上有詳細講解

POJ 1364 King 差分約束系統

這個題目構圖以後, 只須要用bellman_ford判斷是否有負圈.

構圖方法:

首先進行轉換:a[j]+...+a[j+m] = a[1]+...a[j+m] - (a[1]+...+a[j-1]) = sum[j+m] -


sum[j-1] >(<) ki. 差分約束只能所有是<=或者(>=).

第二步轉換: sum[j+m]-sum[j-1] <= ki-1 或者 sum[j-1]-sum[j+m] <= -ki-1.

約束圖構造好後就是簡單的Bellman-Ford了!

POJ 1716 Integer Intervals 是1201的簡單版本, 貪心算法可以獲得更好的效果.

POJ 2983 Is the Information Reliable?

差分約束題, 處理一下等號的狀況, 而後普通的Bellman_ford

POJ 3159 Candies 最短路徑

Bellman-Ford超時, Dijkstra算法能夠高效解決, SPFA(隊列)竟然超時...SPFA修改成堆棧實現就過了.

POJ 3169 Layout 差分約束

Bellman-Ford 和 SPFA 實現都可

POJ 3259 Wormholes 判斷負權迴路

TOJ 2976 Path 單純的最短路徑 可練習SPFA

ZOJ 3033 Board Games 我作的第一道Bellman-Ford題目

首先,DFS判斷可達性,不可達直接輸出infinity結束,可達,bellman-ford判斷是否存在負環,存在輸出infinity,不然,輸出最短距離。

 

SPFA算法模版+鄰接表實現 
SPFA即shotest path faster algorithm,由意思就能夠看出該算法效率比較高。

其實SPFA就是bellman-ford算法的一個優化。

具體作法是用一個隊列保存待鬆弛的點,而後對於每一個出隊的點依次遍歷每一個與他有邊相鄰的點(用鄰接表效率較高),若是該點能夠鬆弛而且隊列中沒有該點則將它加入隊列中,如此迭代直到隊列爲空。

聽說平均效率是O(E),可見對邊稀疏的圖用此算法效果是至關可觀的。

 

若要判負環路,則記錄一個點的入隊次數,若超過邊數,則有負權環。

 

  1 #include <iostream>
  2 #include <queue>
  3 using namespace std;
  4 
  5 const long MAXN=10000;
  6 const long lmax=0x7FFFFFFF;
  7 
  8 typedef struct 
  9 {
 10     long v;
 11     long next;
 12     long cost;
 13 }Edge;
 14 
 15 
 16 Edge e[MAXN];
 17 long p[MAXN];
 18 long Dis[MAXN];
 19 bool vist[MAXN];
 20 
 21 queue<long> q;
 22 
 23 long m,n;//點,邊
 24 void init()
 25 {
 26     long i;
 27     long eid=0;
 28 
 29     memset(vist,0,sizeof(vist));
 30     memset(p,-1,sizeof(p));
 31     fill(Dis,Dis+MAXN,lmax);
 32 
 33     while (!q.empty())
 34     {
 35         q.pop();
 36     }
 37 
 38     for (i=0;i<n;++i)
 39     {
 40         long from,to,cost;
 41         scanf("%ld %ld %ld",&from,&to,&cost);
 42 
 43         e[eid].next=p[from];
 44         e[eid].v=to;
 45         e[eid].cost=cost;
 46         p[from]=eid++;
 47 
 48         //如下適用於無向圖
 49         swap(from,to);
 50         
 51         e[eid].next=p[from];
 52         e[eid].v=to;
 53         e[eid].cost=cost;
 54         p[from]=eid++;
 55 
 56     }
 57 }
 58 
 59 void print(long End)
 60 {
 61     //若爲lmax 則不可達
 62     printf("%ld\n",Dis[End]);    
 63 }
 64 
 65 void SPF()
 66 {
 67 
 68     init();
 69 
 70     long Start,End;
 71     scanf("%ld %ld",&Start,&End);
 72     Dis[Start]=0;
 73     vist[Start]=true;
 74     q.push(Start);
 75 
 76     while (!q.empty())
 77     {
 78         long t=q.front();
 79         q.pop();
 80         vist[t]=false;
 81         long j;
 82         for (j=p[t];j!=-1;j=e[j].next)
 83         {
 84             long w=e[j].cost;
 85             if (w+Dis[t]<Dis[e[j].v])
 86             {
 87                 Dis[e[j].v]=w+Dis[t];
 88                 if (!vist[e[j].v])
 89                 {
 90                     vist[e[j].v]=true;
 91                     q.push(e[j].v);
 92                 }
 93             }
 94         }
 95     }
 96 
 97     print(End);
 98 
 99 }
100 
101 int main()
102 {
103     while (scanf("%ld %ld",&m,&n)!=EOF)
104     {
105         SPF();
106     }
107     return 0;
108 }
View Code

 

 

POJ 1511-Invitation Cards(SPFA算法) 
今天終於用SPFA寫出了第一個程序,感受收穫很大,從Dij到Floyed再到Bellmen,以及今天的SPFA,每一種算法背後都蘊藏着許多值得思考的地方。正由於研究了它們,才使得個人能力不斷地得到了提升。
以前覺得SPFA作爲最短路問題最快的算法,想必代碼定很差寫,不過今天研究過才知道,SPFA的代碼量遠遠不及Dij,這着實使人驚歎,原來最好的算法SPFA是如此的好寫,呵呵 我想此算法在很大程度上能夠徹底代替以前的算法,之後再碰到最短路問題時,SPFA必定能成爲首要的選擇!
PS:因爲是用鄰接表來存儲的,因此每次操做前要收回之前分配的內存,我嘗試了收回和不收回兩種方法,發現其實差異不大,若是純粹是比賽的話,可能不收回反而會更好些(避免超時)。固然若是在實際應用中,應該切記內存的分配,不然軟件可能會發生異常。

 

  1 //Coded by abilitytao 
  2 //Time:2009-04-10 22:49:58
  3 #include<iostream>
  4 #include<cmath>
  5 #include<queue>
  6 using namespace std;
  7 #define MAX_NUM 1000000001
  8 #define MAX_DOTNUM 1000001
  9 
 10 int n,m;
 11 queue<int>myqueue;
 12 bool mark[MAX_DOTNUM];
 13 __int64 dis[MAX_DOTNUM];
 14 
 15 
 16 struct node
 17 {
 18 
 19     int v;
 20     int w;
 21     node *next;
 22 }edge[MAX_DOTNUM];//此鄰接表用於存儲正向圖
 23 
 24 node reversed_edge[MAX_DOTNUM];//此逆鄰接表用於存儲逆向圖
 25 
 26 void initial(node edge[])//鄰接表的初始化,裏面封裝了回收上一次操做所分配以內存的操做
 27 {
 28     int i;
 29     node *p;
 30     node *q;
 31     for(i=1;i<=n;i++)
 32     {
 33         p=&edge[i];
 34         q=p->next;
 35         while(q!=NULL)
 36         {
 37             p->next=q->next;
 38             delete q;
 39             q=p->next;
 40         }
 41     }
 42 }
 43 
 44 
 45 void input_case()//每個case的輸入函數
 46 {
 47 
 48     int i;
 49     for(i=1;i<=m;i++)
 50     {
 51         node *p;
 52         node *q;
 53         int a,b,c;
 54         scanf("%d%d%d",&a,&b,&c);
 55         /**/////////////////////////
 56         p=&edge[a];
 57         q=new node;
 58         q->v=b;
 59         q->w=c;
 60         q->next=p->next;
 61         p->next=q;
 62         /**/////////////////////////
 63         p=&reversed_edge[b];
 64         q=new node;
 65         q->v=a;
 66         q->w=c;
 67         q->next=p->next;
 68         p->next=q;
 69     }
 70 }
 71 
 72 
 73 void spfa(node edge[])//SPFA部分
 74 {
 75 
 76     int i;
 77     /**////////////////////////////////////////////////////////////////
 78     memset(mark,false,sizeof(mark));
 79     for(i=1;i<=n;i++)
 80         dis[i]=MAX_NUM;
 81     while(myqueue.size()!=0)
 82         myqueue.pop();
 83     /**////////////////////////////////////////////////////////////
 84     dis[1]=0;
 85     mark[1]=true;
 86     myqueue.push(1);
 87     while(myqueue.size()!=0)//若是隊列不空,則進行鬆弛操做,直到隊列空爲止
 88     {
 89         int temp=myqueue.front();
 90         myqueue.pop();
 91         mark[temp]=false;
 92         node *p;
 93         for(p=edge[temp].next;p!=NULL;p=p->next)
 94         {
 95             if(dis[p->v]>dis[temp]+p->w)
 96             {
 97                 dis[p->v]=dis[temp]+p->w;
 98                 if(mark[p->v]!=true)
 99                 {
100                     myqueue.push(p->v);
101                     mark[p->v]=true;
102                 }
103             }
104         }
105     }
106 }
107 
108 
109 int main()
110 {
111 
112     int testcase;
113     int i,j;
114     __int64 sum;
115     scanf("%d",&testcase);
116     for(i=1;i<=MAX_DOTNUM-1;i++)
117     {
118         edge[i].v=i;
119         edge[i].w=0;
120         edge[i].next=NULL;
121     }
122     for(i=1;i<=MAX_DOTNUM-1;i++)
123     {
124         reversed_edge[i].v=i;
125         reversed_edge[i].w=0;
126         reversed_edge[i].next=NULL;
127     }
128     for(i=1;i<=testcase;i++)
129     {
130         sum=0;
131         scanf("%d%d",&n,&m);
132         initial(edge);
133         initial(reversed_edge);
134         input_case();
135         spfa(edge);
136         for(j=1;j<=n;j++)
137             sum+=dis[j];
138         spfa(reversed_edge);
139         for(j=1;j<=n;j++)
140             sum+=dis[j];
141         printf("%I64d\n",sum);
142     }
143     system("pause");
144     return 0;
145 
146 }
View Code
相關文章
相關標籤/搜索