最短路—SPFA

求单源最短路的SPFA算法的全称是:Shortest Path Faster Algorithm。
SPFA算法是西南交通大学段凡丁于1994年发表的.
很多时候,给定的图存在负权边,这时类似Dijkstra等算法便没有了用武之地,而Bellman-Ford算法的复杂度又过高,SPFA算法便派上用场了。
    我们用数组d记录每个结点的最短路径估计值,而且用邻接表来存储图G。
我们采取的方法是动态逼近法:设立一个先进先出的队列用来保存待优化的结点,优化时每次取出队首结点u,并且用u点当前的最短路径估计值对离开u点所指向的结点v进行松弛操作,如果v点的最短路径估计值有所调整,且v点不在当前的队列中,就将v点放入队尾。
这样不断从队列中取出结点来进行松弛操作,直至队列空为止。
   定理: 只要最短路径存在,上述SPFA算法必定能求出最小值。
    期望的时间复杂度O(ke), 其中k为所有顶点进队的平均次数,可以证明k一般小于等于2。 
    实现方法:建立一个队列,初始时队列里只有起始点,再建立一个表格记录起始点到所有点的最短路径(该表格的初始值要赋为极大值,该点到他本身的路径赋为0)。然后执行松弛操作,用队列里有的点去刷新起始点到所有点的最短路,如果刷新成功且被刷新点不在队列中则把该点加入到队列最后。

重复执行直到队列为空

判断有无负环:如果某个点进入队列的次数超过N次则存在负环(SPFA无法处理带负环的图)

模版:

 

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4  using  namespace std;
 5 typedef  struct node
 6 {
 7      long adj,data; /* adj为邻接点,data为边的权值 */
 8      struct node *next;
 9 }node;
10 node *g[ 1000];
11  long dis[ 1000],flag[ 1000],q[ 1000],n,e; /* dis记录最短路径值,flag做出入队标记,q为队列 */
12  void spfa( long st)
13 {
14      long i,k,front,rear;
15     node *p;
16      for(i= 0;i<n;i++) 
17         dis[i]= 0x7fffffff; /* 初始化路径为长整型最大数 */
18     memset(flag, 0, sizeof(flag)); /* 入队标记初始化为0 */
19     front= 0;
20     rear= 1;
21      /* 将起始点入队 */
22     dis[st]= 0; /* 到自己距离为0 */
23     q[rear]=st; /* 入队 */ 
24     flag[st]= 1; /* 置入队标记 */
25      while(front!=rear) /* 当队列不为空时 */
26     {     
27         front=(front+ 1)% 1000; /* 出队,注意使用循环队列 */
28         k=q[front]; /* 记下出队点号 */
29         flag[k]= 0; /* 置出队标记 */
30         p=g[k]; /* 找到以出队点为头的邻接链表,考察由出队点中转至各邻接点路径是否更短 */
31          while(p)
32         {
33              if(dis[k]+p->data<dis[p->adj])
34             { 
35                 dis[p->adj]=dis[k]+p->data; /* 路径更短,更新路径值 */
36                  if(!flag[p->adj]) /* 若其邻接点未入队 */
37                 { 
38                     rear=(rear+ 1)% 1000; /* 入队 */
39                     q[rear]=p->adj;
40                     flag[p->adj]= 1; /* 置入队标记 */
41                 }
42             }
43             p=p->next; /* 记得改变循环变量,考察下一个邻接点 */
44         }
45     }  
46 }
47  int main()
48 {
49      long i,x,y,z;
50     node *p;
51     scanf( " %ld%ld ",&n,&e); /* 读入点数,边数 */
52     memset(g, 0, sizeof(g)); /* 初始化图 */
53      for(i= 1;i<=e;i++) /* 读入边的信息,左点、右点、权值 */
54     {
55         scanf( " %ld%ld%ld ",&x,&y,&z);
56         p=(node*)malloc( sizeof(node)); /* 创建链表 */
57         p->data=z;
58         p->adj=y;
59         p->next=g[x]; /* 尾插法 */
60         g[x]=p;
61     }
62     spfa( 0); /* 计算由起始点到各点的最短路径 */
63      for(i= 0;i<n;i++)
64         printf( " %ld  ",dis[i]);
65     printf( " \n ");
66      return  0;
67 }

 

POJ 3259

 

View Code
  1 #include <iostream>
  2 #include <cstring>
  3 #include <cstdlib>
  4 #include <cstdio>
  5 #include <queue>
  6  using  namespace std;
  7 
  8  const  int N= 501;
  9  const  int NN= 100001;
 10  const  int inf= 0x7fffffff;
 11 queue< int> qu;
 12  int n,nu;
 13 
 14 typedef  struct node
 15 {
 16      int adj,val;
 17      struct node *next;
 18 }node;
 19 node node[NN],*p[N];
 20 
 21  int SPFA()
 22 {
 23      int x,i,a,b;
 24      int vis[N],dis[N],num[N];
 25      struct node *head[N];
 26      for(i= 1;i<=n;i++)
 27     {
 28         vis[i]= 0;
 29         num[i]= 0;
 30         dis[i]=inf;
 31         head[i]=p[i];
 32     }
 33     dis[ 1]= 0;
 34     vis[ 1]= 1
 35     num[ 1]++;
 36     qu.push( 1);
 37      while(!qu.empty())
 38     {
 39         x=qu.front();
 40         qu.pop();
 41         vis[x]= 0;
 42         head[x]=p[x];
 43          while(head[x])
 44         {
 45             a=head[x]->adj;
 46             b=head[x]->val;
 47              if(dis[a]>dis[x]+b)
 48             {
 49                 dis[a]=dis[x]+b;
 50                  if(!vis[a])
 51                 {
 52                     qu.push(a);
 53                     vis[a]= 1;
 54                     num[a]++;
 55                      if(num[a]>=n) // 如果入队的次数超过总数,说明存在回路 
 56                           return  1;
 57                 }
 58             }
 59             head[x]=head[x]->next; 
 60         }
 61     }
 62      return  0;
 63 }
 64 
 65  int main()
 66 {
 67      int t,i,m,w,a,b,c;
 68     scanf( " %d ",&t);
 69      while(t--)
 70     {
 71          while(!qu.empty()) 
 72             qu.pop();
 73         memset(node, 0, sizeof(node));
 74         memset(p, 0, sizeof(p));
 75         nu= 0;
 76         scanf( " %d%d%d ",&n,&m,&w);
 77          for(i= 0;i<m;i++)
 78         {
 79             scanf( " %d%d%d ",&a,&b,&c);
 80             node[nu].adj=b;  
 81             node[nu].val=c;
 82             node[nu].next=p[a];  
 83             p[a]=&node[nu];  
 84             nu++;  
 85             node[nu].adj=a;  
 86             node[nu].val=c;  
 87             node[nu].next=p[b];  
 88             p[b]=&node[nu];  
 89             nu++; 
 90         }
 91          for(i= 0;i<w;i++)
 92         {
 93             scanf( " %d%d%d ",&a,&b,&c);
 94             node[nu].adj=b;  
 95             node[nu].val=-c;
 96             node[nu].next=p[a];
 97             p[a]=&node[nu];  
 98             nu++;  
 99         }
100          if(SPFA()) 
101             puts( " YES ");
102          else 
103             puts( " NO ");
104     }
105      return  0;
106 }

 

 

 

 

你可能感兴趣的:(SPFA)