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