posts - 195, comments - 34, trackbacks - 0, articles - 1

          最短路徑 之 SPFA算法 zz

          Posted on 2009-10-30 14:00 小強摩羯座 閱讀(2959) 評論(0)  編輯  收藏 所屬分類: 算法編程
          最短路徑 之 SPFA算法 (轉載)(2009-05-06 20:41:51)

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

          SPFA是這篇日志要寫的一種算法,它的性能非常好,代碼實現也并不復雜。特別是當圖的規模大,用鄰接矩陣存不下的時候,用SPFA則可以很方便地面對臨接表。每個人都寫過廣搜,SPFA的實現和廣搜非常相似。

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

          首先說明,SPFA是一種單源最短路徑算法,所以以下所說的“某點的最短路徑長度”,指的是“某點到源點的最短路徑長度”。

          我們記源點為S,由源點到達點i的“當前最短路徑”為D[i],開始時將所有D[i]初始化為無窮大,D[S]則初始化為0。算法所要做的,就是在運行過程中,不斷嘗試減小D[]數組的元素,最終將其中每一個元素減小到實際的最短路徑。

          過程中,我們要維護一個隊列,開始時將源點置于隊首,然后反復進行這樣的操作,直到隊列為空:

          (1)從隊首取出一個結點u,掃描所有由u結點可以一步到達的結點,具體的掃描過程,隨存儲方式的不同而不同;

          (2)一旦發現有這樣一個結點,記為v,滿足D[v] > D[u] + w(u, v),則將D[v]的值減小,減小到和D[u] + w(u, v)相等。其中,w(u, v)為圖中的邊u-v的長度,由于u-v必相鄰,所以這個長度一定已知(不然我們得到的也不叫一個完整的圖);這種操作叫做松弛。

          引用內容 引用內容
          松弛操作的原理是著名的定理:“三角形兩邊之和大于第三邊”,在信息學中我們叫它三角不等式。所謂對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.

          【參考程序】:
          #include<stdio.h>
          #include<string.h>
          #include<stdlib.h>
          long queue[1001],a[1001],psum[1001],dis[1001],l[1001][1001],cost[1001][1001];
          long n,m,s,i,j;
          bool hash[1001],bk;
          void spfa(int s)
          {
              int head,tail,start,now,i;
              for (i=1;i<=n;i++)
              {
                  dis[i]=0xfffffff;
                  psum[i]=0;
                  hash[i]=false;
              }
              head=tail=1;hash[s]=true;
              psum[s]=1;dis[s]=0;queue[1]=s;
              while (head<=tail)
              {
                  start=queue[(head-1)%n+1];
                  hash[start]=true;
                  for (i=1;i<=l[start][0];i++)
                  {
                      now=l[start][i];
                      if (dis[now]>dis[start]+cost[start][now])
                      {
                          dis[now]=dis[start]+cost[start][now];
                          if (!hash[now])
                          {
                              hash[now]=true;
                              tail++;
                              queue[(tail-1)%n+1]=now;
                              psum[now]++;
                              if (psum[now]>n)
                              {//記錄每個點進隊的次數(判斷環的關鍵}
                                  bk=false;
                                  return;
                              }
                          }
                      }
                  }
                  head++;
                  hash[start]=false;
                  if (dis[s]<0)
                  {//判斷環的一個優化
                      bk=false;
                      return;
                  }
              }
          }
          void output()
          {
              bk=true;
              spfa(s);
              if (!bk)
              {
                  printf("-1\n");
                  return;
              }
              memcpy(a,dis,sizeof(long)*(n+1));
              for (i=1;i<=n;i++)
                if (a[i]==0xfffffff)
                {
                      bk=true;
                      spfa(i);
                      if (!bk)
                      {
                          printf("-1\n");
                          return;
                      }
                }
              for (i=1;i<=n;i++)
                if (a[i]==0xfffffff) printf("NoPath\n");
                else printf("%d\n",a[i]);
          }
          void input()
          {
              scanf("%d%d%d",&n,&m,&s);
              for (i=1;i<=n;i++)
                for (j=1;j<=n;j++)
                  if (i==j) cost[i][j]=0;
                  else cost[i][j]=0xfffffff;
              memset(l,0,sizeof(l));
              int x,y,c;
              for (i=1;i<=m;i++)
              {
                  scanf("%d%d%d",&x,&y,&c);
                  if (c<cost[x][y])
                  {
                      cost[x][y]=c;
                      l[x][0]++;
                      l[x][l[x][0]]=y;
                  }
              }
          }
          int main()
          {
              input();
              output();
              system("pause");
              return 0;
          }

           

          本文來自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),可見對邊稀疏的圖用此算法效果是相當可觀的。

           

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

           

          #include <iostream>
          #include 
          <queue>
          using namespace std;

          const long MAXN=10000;
          const long lmax=0x7FFFFFFF;

          typedef 
          struct  
          {
              
          long v;
              
          long next;
              
          long cost;
          }
          Edge;


          Edge e[MAXN];
          long p[MAXN];
          long Dis[MAXN];
          bool vist[MAXN];

          queue
          <long> q;

          long m,n;//點,邊
          void init()
          {
              
          long i;
              
          long eid=0;

              memset(vist,
          0,sizeof(vist));
              memset(p,
          -1,sizeof(p));
              fill(Dis,Dis
          +MAXN,lmax);

              
          while (!q.empty())
              
          {
                  q.pop();
              }


              
          for (i=0;i<n;++i)
              
          {
                  
          long from,to,cost;
                  scanf(
          "%ld %ld %ld",&from,&to,&cost);

                  e[eid].next
          =p[from];
                  e[eid].v
          =to;
                  e[eid].cost
          =cost;
                  p[from]
          =eid++;

                  
          //以下適用于無向圖
                  swap(from,to);
                  
                  e[eid].next
          =p[from];
                  e[eid].v
          =to;
                  e[eid].cost
          =cost;
                  p[from]
          =eid++;

              }

          }


          void print(long End)
          {
              
          //若為lmax 則不可達
              printf("%ld\n",Dis[End]);    
          }


          void SPF()
          {

              init();

              
          long Start,End;
              scanf(
          "%ld %ld",&Start,&End);
              Dis[Start]
          =0;
              vist[Start]
          =true;
              q.push(Start);

              
          while (!q.empty())
              
          {
                  
          long t=q.front();
                  q.pop();
                  vist[t]
          =false;
                  
          long j;
                  
          for (j=p[t];j!=-1;j=e[j].next)
                  
          {
                      
          long w=e[j].cost;
                      
          if (w+Dis[t]<Dis[e[j].v])
                      
          {
                          Dis[e[j].v]
          =w+Dis[t];
                          
          if (!vist[e[j].v])
                          
          {
                              vist[e[j].v]
          =true;
                              q.push(e[j].v);
                          }

                      }

                  }

              }


              print(End);

          }


          int main()
          {
              
          while (scanf("%ld %ld",&m,&n)!=EOF)
              
          {
                  SPF();
              }

              
          return 0;
          }
          0
          0
          (請您對文章做出評價)

          一、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算法

          二、             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|次表示有負權。

          三、             學以致用

          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),可見對邊稀疏的圖用此算法效果是相當可觀的。

           

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

           

          #include <iostream>
          #include 
          <queue>
          using namespace std;

          const long MAXN=10000;
          const long lmax=0x7FFFFFFF;

          typedef 
          struct  
          {
              
          long v;
              
          long next;
              
          long cost;
          }
          Edge;


          Edge e[MAXN];
          long p[MAXN];
          long Dis[MAXN];
          bool vist[MAXN];

          queue
          <long> q;

          long m,n;//點,邊
          void init()
          {
              
          long i;
              
          long eid=0;

              memset(vist,
          0,sizeof(vist));
              memset(p,
          -1,sizeof(p));
              fill(Dis,Dis
          +MAXN,lmax);

              
          while (!q.empty())
              
          {
                  q.pop();
              }


              
          for (i=0;i<n;++i)
              
          {
                  
          long from,to,cost;
                  scanf(
          "%ld %ld %ld",&from,&to,&cost);

                  e[eid].next
          =p[from];
                  e[eid].v
          =to;
                  e[eid].cost
          =cost;
                  p[from]
          =eid++;

                  
          //以下適用于無向圖
                  swap(from,to);
                  
                  e[eid].next
          =p[from];
                  e[eid].v
          =to;
                  e[eid].cost
          =cost;
                  p[from]
          =eid++;

              }

          }


          void print(long End)
          {
              
          //若為lmax 則不可達
              printf("%ld\n",Dis[End]);    
          }


          void SPF()
          {

              init();

              
          long Start,End;
              scanf(
          "%ld %ld",&Start,&End);
              Dis[Start]
          =0;
              vist[Start]
          =true;
              q.push(Start);

              
          while (!q.empty())
              
          {
                  
          long t=q.front();
                  q.pop();
                  vist[t]
          =false;
                  
          long j;
                  
          for (j=p[t];j!=-1;j=e[j].next)
                  
          {
                      
          long w=e[j].cost;
                      
          if (w+Dis[t]<Dis[e[j].v])
                      
          {
                          Dis[e[j].v]
          =w+Dis[t];
                          
          if (!vist[e[j].v])
                          
          {
                              vist[e[j].v]
          =true;
                              q.push(e[j].v);
                          }

                      }

                  }

              }


              print(End);

          }


          int main()
          {
              
          while (scanf("%ld %ld",&m,&n)!=EOF)
              
          {
                  SPF();
              }

              
          return 0;
          }

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

          //Coded by abilitytao 
          //Time:2009-04-10 22:49:58
          #include<iostream>
          #include
          <cmath>
          #include
          <queue>
          using namespace std;
          #define MAX_NUM 1000000001
          #define MAX_DOTNUM 1000001

          int n,m;
          queue
          <int>myqueue;
          bool mark[MAX_DOTNUM];
          __int64 dis[MAX_DOTNUM];


          struct node
          {

              
          int v;
              
          int w;
              node 
          *next;
          }
          edge[MAX_DOTNUM];//此鄰接表用于存儲正向圖

          node reversed_edge[MAX_DOTNUM];
          //此逆鄰接表用于存儲逆向圖

          void initial(node edge[])//鄰接表的初始化,里面封裝了回收上一次操作所分配之內存的操作
          {
              
          int i;
              node 
          *p;
              node 
          *q;
              
          for(i=1;i<=n;i++)
              
          {
                  p
          =&edge[i];
                  q
          =p->next;
                  
          while(q!=NULL)
                  
          {
                      p
          ->next=q->next;
                      delete q;
                      q
          =p->next;
                  }

              }

          }



          void input_case()//每一個case的輸入函數
          {

              
          int i;
              
          for(i=1;i<=m;i++)
              
          {
                  node 
          *p;
                  node 
          *q;
                  
          int a,b,c;
                  scanf(
          "%d%d%d",&a,&b,&c);
                  
          ////////////////////////
                  p=&edge[a];
                  q
          =new node;
                  q
          ->v=b;
                  q
          ->w=c;
                  q
          ->next=p->next;
                  p
          ->next=q;
                  
          ////////////////////////
                  p=&reversed_edge[b];
                  q
          =new node;
                  q
          ->v=a;
                  q
          ->w=c;
                  q
          ->next=p->next;
                  p
          ->next=q;
              }

          }



          void spfa(node edge[])//SPFA部分
          {

              
          int i;
              
          ///////////////////////////////////////////////////////////////
              memset(mark,false,sizeof(mark));
              
          for(i=1;i<=n;i++)
                  dis[i]
          =MAX_NUM;
              
          while(myqueue.size()!=0)
                  myqueue.pop();
              
          ///////////////////////////////////////////////////////////
              dis[1]=0;
              mark[
          1]=true;
              myqueue.push(
          1);
              
          while(myqueue.size()!=0)//如果隊列不空,則進行松弛操作,直到隊列空為止
              {
                  
          int temp=myqueue.front();
                  myqueue.pop();
                  mark[temp]
          =false;
                  node 
          *p;
                  
          for(p=edge[temp].next;p!=NULL;p=p->next)
                  
          {
                      
          if(dis[p->v]>dis[temp]+p->w)
                      
          {
                          dis[p
          ->v]=dis[temp]+p->w;
                          
          if(mark[p->v]!=true)
                          
          {
                              myqueue.push(p
          ->v);
                              mark[p
          ->v]=true;
                          }

                      }

                  }

              }

          }



          int main()
          {

              
          int testcase;
              
          int i,j;
              __int64 sum;
              scanf(
          "%d",&testcase);
              
          for(i=1;i<=MAX_DOTNUM-1;i++)
              
          {
                  edge[i].v
          =i;
                  edge[i].w
          =0;
                  edge[i].next
          =NULL;
              }

              
          for(i=1;i<=MAX_DOTNUM-1;i++)
              
          {
                  reversed_edge[i].v
          =i;
                  reversed_edge[i].w
          =0;
                  reversed_edge[i].next
          =NULL;
              }

              
          for(i=1;i<=testcase;i++)
              
          {
                  sum
          =0;
                  scanf(
          "%d%d",&n,&m);
                  initial(edge);
                  initial(reversed_edge);
                  input_case();
                  spfa(edge);
                  
          for(j=1;j<=n;j++)
                      sum
          +=dis[j];
                  spfa(reversed_edge);
                  
          for(j=1;j<=n;j++)
                      sum
          +=dis[j];
                  printf(
          "%I64d\n",sum);
              }

              system(
          "pause");
              
          return 0;

          }




          主站蜘蛛池模板: 安平县| 漠河县| 江阴市| 嘉峪关市| 门头沟区| 凤台县| 灵武市| 七台河市| 龙岩市| 丽江市| 错那县| 枣强县| 贵州省| 六枝特区| 高淳县| 兰坪| 伊宁县| 边坝县| 讷河市| 芒康县| 秭归县| 永平县| 临清市| 娱乐| 遵义县| 泽库县| 新巴尔虎左旗| 芷江| 蕉岭县| 桐梓县| 宣威市| 尉氏县| 黄龙县| 富蕴县| 桑日县| 南川市| 泾源县| 天等县| 田阳县| 腾冲县| 瓮安县|