图论-SPFA
复习笔记-SPFA求单源最短路
一本通上的SPFA用的是邻接矩阵,而前向星的SPFA更为常用,所以作为巩固复习和改进,我写了这篇随笔。
SPFA的基本操作
基本原理
动态逼近法。
实现思路
设立一个先进先出的队列用来保存待优化的结点,优化时每次取出队首结点u,并且用u点当前的最短路径估计值对离开u点所指向的结点v进行松弛操作,如果v点的最短路径估计值有所调整,且v点不在当前的队列中,就将v点放入队尾。这样不断从队列中取出结点来进行松弛操作,直至队列空为止。——度娘
那么松弛操作是个啥玩意儿呢?
如果IK + KJ < IJ
那么IJ = IK + KJ
也就是说,如果经过一个中转点到达终点比当前记录的两点最短路还要近,那么就把这个长度更新为两点最短路。
伪代码
1 SPFA(X){ 2 初始化:d[x][i]赋值为INF; d[x][x]=0; 3 x入队; 更新visit; 4 while(队里有元素){ 5 k = 队首; 队首出队; 6 while(i = 遍历以k为起点的所有边){ 7 取出 j = i; 8 if(XK + KJ < XJ){ 9 XJ = XK + KJ; 10 if(j不在队里,也就是!visit[j]){ 11 j入队; 12 更新visit; 13 } 14 } 15 } 16 } 17 }
程序实现
基本程序
如果d[x][y]存储x到y的路长,断路存为INF,visit来表示一个点在不在队列内,那么我们可以这样实现SPFA
1 void spfa(int x){ 2 d[x][x] = 0; 3 queue <int> nod; nod.push(x); visit[x] = 1; 4 while(nod.size()){ 5 int k = nod.front(); nod.pop(); visit[k] = 0; 6 for(int j = 1; j <= n; j++){ 7 if(d[x][j] > d[x][k] + d[k][j]){ 8 d[x][j] = d[x][k] + d[k][j]; 9 if(!visit[j]){ 10 nod.push(j); 11 visit[j]=1; 12 } 13 } 14 } 15 } 16 }
与链式前向星的结合
很明显上面的程序是针对用邻接矩阵存图的方式,那么如果我们使用链式前向星(如果不知道的话看这里)
用d[i]存储x到i的最短路长,初始化赋值为INF,d[x]赋值为0,visit[i]记录i是否在队列,那么程序是这样的(to为终点v为边权next为下一条边,head[i]为i为起点的第一条边)
1 void spfa(int x){ 2 fill(d + 1, d + n + 1, INF); d[x] = 0; 3 queue <int> nod; nod.push(x); visit[x] = 1; 4 while(nod.size()){ 5 int k = nod.front(); nod.pop(); visit[k] = 0; 6 for(int i = head[k]; i != 0; i = edge[i].next){ 7 int j = edge[i].to; 8 if(d[j] > d[k] + edge[i].w){ 9 d[j] = d[k] + edge[i].w; 10 if(!visit[j]){ 11 nod.push(j); 12 visit[j]=1; 13 } 14 } 15 } 16 } 17 }
这样就可以求出来x到每个点的最短路长了嘤。