标签:
1 #include<cstdio> 2 #include<cstring> 3 #include<queue> 4 #include<iostream> 5 #include<algorithm> 6 #include<cmath> 7 using namespace std; 8 #define N 200000+5 9 const int inf=0x3f3f3f3f; 10 struct node{ 11 int u,v,nest,w; 12 }e[N]; 13 int head[N],dis[N],vis[N]; 14 int n,m,cnt; 15 16 void add(int u,int v,int w) 17 { 18 e[cnt].u=u; 19 e[cnt].v=v; 20 e[cnt].w=w; 21 e[cnt].nest=head[u]; 22 head[u]=cnt++; 23 } 24 25 void spfa() 26 { 27 int i,u,v,w; 28 queue<int>q; 29 for(i=1;i<=n;i++) 30 { 31 dis[i]=inf; vis[i]=0; 32 } 33 dis[1]=0; 34 vis[1]=1; 35 q.push(1); 36 while(!q.empty()) 37 { 38 u=q.front(); 39 q.pop(); 40 vis[u]=0; 41 for(i=head[u];i!=-1;i=e[i].nest) 42 { 43 v=e[i].v; 44 w=e[i].w; 45 if(dis[v]>dis[u]+w) 46 { 47 dis[v]=dis[u]+w; 48 if(!vis[v]) 49 { 50 vis[v]=1; 51 q.push(v); 52 } 53 } 54 } 55 } 56 } 57 58 int main() 59 { 60 int i,u,v,w; 61 while(~scanf("%d%d",&n,&m)) 62 { 63 cnt=0; 64 memset(head,-1,sizeof(head)); 65 for(i=0;i<m;i++) 66 { 67 scanf("%d%d%d",&u,&v,&w); 68 add(u,v,w); 69 } 70 spfa(); 71 for(i=2;i<=n;i++) 72 printf("%d\n",dis[i]); 73 74 } 75 return 0; 76 }
很多时候,给定的图存在负权边,这时类似Dijkstra等算法便没有了用武之地,而Bellman-Ford算法的复杂度又过高,SPFA算法便派上用场了。
简洁起见,我们约定有向加权图G不存在负权回路,即最短路径一定存在。当然,我们可以在执行该算法前做一次拓扑排序,以判断是否存在负权回路,但这不是我们讨论的重点。
我们用数组d记录每个结点的最短路径估计值,而且用邻接表来存储图G。我们采取的方法是动态逼近法:设立一个先进先出的队列用来保存待优化的结点,优化时每次取出队首结点u,并且用u点当前的最短路径估计值对离开u点所指向的结点v进行松弛操作,如果v点的最短路径估计值有所调整,且v点不在当前的队列中,就将v点放入队尾。这样不断从队列中取出结点来进行松弛操作,直至队列空为止。
定理: 只要最短路径存在,上述SPFA算法必定能求出最小值。
证明:每次将点放入队尾,都是经过松弛操作达到的。换言之,每次的优化将会有某个点v的最短路径估计值d[v]变小。所以算法的执行会使d越来越小。由于我们假定图中不存在负权回路,所以每个结点都有最短路径值。因此,算法不会无限执行下去,随着d值的逐渐变小,直到到达最短路径值时,算法结束,这时的最短路径估计值就是对应结点的最短路径值。(证毕)
期望的时间复杂度O(ke), 其中k为所有顶点进队的平均次数,可以证明k一般小于等于2。
实现方法:建立一个队列,初始时队列里只有起始点,在建立一个表格记录起始点到所有点的最短路径(该表格的初始值要赋为极大值,该点到他本身的路径赋为0)。然后执行松弛操作,用队列里有的点去刷新起始点到所有点的最短路,如果刷新成功且被刷新点不在队列中则把该点加入到队列最后。重复执行直到队列为空
判断有无负环:如果某个点进入队列的次数超过N次则存在负环(SPFA无法处理带负环的图)
摘自————百度百科
标签:
原文地址:http://www.cnblogs.com/WDKER/p/5161562.html