求单源最短路的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 }