最短路—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无法处理带负环的图)

模版:

View Code
 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  }

poj3259

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

 我的代码

View Code
 1 #include <stdio.h>
 2 #include <string.h>
 3 #define maxn 500*10000+50;
 4 int map[505][505];
 5 int vis[505];//是否在队伍中
 6 int num[505];//记录进队的次数
 7 int dis[505];
 8 int q[500005],f,r;
 9 void init(int n)
10 {
11     int i,j;
12     for(i = 1;i <= n;i++)
13     {
14         dis[i] = maxn;
15         for(j = 1;j <= n;j++)
16             map[i][j] = maxn;
17     }
18     
19 }
20 int spfa(int n)
21 {
22     memset(num,0,sizeof(num));
23     memset(vis,0,sizeof(vis));//初始化
24     f = r = 0;
25     int i;
26     q[r++] = 1;
27     num[1] = 1;
28     vis[1] = 1;//进队
29     dis[1] = 0;//离自己的距离为0
30     while(f < r)
31     {
32         int x;
33         x = q[f++];
34         vis[x] = 0;
35         for(i = 1;i <= n;i++)
36         {
37             if(dis[i] > dis[x]+map[x][i])//跟dis差不多。
38             {
39                 dis[i] = dis[x]+map[x][i];
40                 if(!vis[i])
41                 {
42                     vis[i] = 1;
43                     q[r++] = i;
44                     num[i]++;
45                     if(num[i] == n)//如果入队次数等于n说明有负环
46                     return 1;
47                 }
48             }
49         }
50     }
51     return 0;
52 }
53 int main()
54 {
55     int T,i,j,n,m,w;
56     scanf("%d",&T);
57     while(T--)
58     {
59         int a,b,c;
60         scanf("%d %d %d",&n,&m,&w);
61         init(n);
62 
63         for(i = 1;i <= m;i++)
64         {
65             scanf("%d %d %d",&a,&b,&c);
66             if(map[a][b] > c)
67             map[a][b] = map[b][a] = c;
68         }
69         while(w--)
70         {
71             scanf("%d%d%d",&a,&b,&c);
72             if(map[a][b] > -c)
73             map[a][b] = -c;
74         }
75         int leap;
76         leap = spfa(n);
77         if(leap)
78         printf("YES\n");
79         else
80         puts("NO");
81     }
82     return 0;
83 }

 

posted @ 2012-08-15 16:09  某某。  阅读(393)  评论(0编辑  收藏  举报