SPFA是也一种单源最短路算法,与Dijkstra不同的是,他可以处理负边权,而且能判断负环。SPFA是Bellman算法的队列优化,在过程上和BFS有些类似。SPFA的算法流程大时这样的,先将源点加入队列中,只要队列不为空,取出队首元素,用他去更新与他相连的点的最短路,若成功更新且被更新的点不在队列当中,就将他加入队列。因为每次松弛一定可以使得某个结点的最短路更接近答案,因此算法一定可以结束,最短路上一定没有正环,因为每个结点最多进入队列n-1次,所以可以记录结点被取出的次数,若超过n-1次,说明图中存在负环,不存在最短路。SPFA的算法复杂度比较玄,在稀疏图上一般为O(km),k是较小的常数,但在稠密图上就可能退化到O(nm),可以构造数据故意卡掉SPFA,有兴趣可以自行了解一下,在边权为正的情况下,最好选择堆优化的Dijkstra。SPFA还有两种优化,SLF和LLL,不过还是可以卡掉,而且算法竞赛一般用不到。
1 #include<cstdio> 2 #include<cctype> 3 #include<cstring> 4 #include<queue> 5 using namespace std; 6 const int inf=0x3f3f3f3f; 7 int n,m,s,head[maxn],eid,d[maxn],inq[maxn],cnt[maxn]; 8 struct edge { 9 int v,w,next; 10 edge(int v=0,int w=-1):v(v),w(w) {} 11 } E[maxm]; 12 void init() { //还是不要忘记初始化! 13 memset(head,-1,sizeof(head)); 14 memset(d,inf,sizeof(d)); 15 } 16 void insert(int u,int v,int w) { 17 E[eid]=edge(v,w); 18 E[eid].next=head[u]; 19 head[u]=eid++; 20 } //邻接表存储,时间和空间上都会提高效率 21 queue<int> q; 22 bool spfa() { 23 d[s]=0; 24 q.push(s); //初始先将源点入队 25 inq[s]=1; 26 while(!q.empty()) { 27 int u=q.front(); 28 q.pop(); 29 inq[u]=0; 30 for(int p=head[u];p+1;p=E[p].next) { 31 int v=E[p].v; 32 if(d[v]>d[u]+E[p].w) { //松弛 33 d[v]=d[u]+E[p].w; 34 if(++cnt[v]>=n) return false; //如果一个点被更新超过n-1次 35 //说明存在负环,不存在最短路 36 if(!inq[v]) { //结点不在队列中就可以入队 37 q.push(v); //同一结点可能会多次入队 38 inq[v]=1; //但不会同时出现在队列中 39 } 40 } 41 } 42 } 43 return true; 44 } 45 int main() { 46 scanf("%d%d%d",&n,&m,&s); 47 init(); 48 int u,v,w; 49 for(int i=1;i<=m;++i) { 50 scanf("%d%d%d",&u,&v,&w); 51 insert(u,v,w); 52 } 53 spfa(); 54 for(int i=1;i<=n;++i) { 55 if(i!=1) putchar(' '); 56 printf("%d",d[i]); 57 } 58 return 0; 59 }