SPFA

编程学习

Posted by simplex on March 27, 2021

BELLMAN-FORD & SPFA

大二学的全部还给了HDU,用于存在负权边的问题

直接代码

/*
SPFA(Shortest Path Faster Algorithm) [图的存储方式为邻接表]
是Bellman-Ford算法的一种队列实现,减少了不必要的冗余计算。
算法大致流程是用一个队列来进行维护。 初始时将源加入队列。 每次从队列中取出一个元素,
并对所有与他相邻的点进行松弛,若某个相邻的点松弛成功,则将其入队。 直到队列为空时算法结束。
它可以在O(kE)的时间复杂度内求出源点到其他所有点的最短路径,可以处理负边。
SPFA 在形式上和BFS非常类似,不同的是BFS中一个点出了队列就不可能重新进入队列,但是SPFA中
一个点可能在出队列之后再次被放入队列,也就是一个点改进过其它的点之后,过了一段时间可能本
身被改进,于是再次用来改进其它的点,这样反复迭代下去。
判断有无负环:如果某个点进入队列的次数超过V次则存在负环(SPFA无法处理带负环的图)。
SPFA算法有两个优化算法 SLF 和 LLL:
SLF:Small Label First 策略,设要加入的节点是j,队首元素为i,若dist(j)<dist(i),则将j插入队首,
否则插入队尾。
LLL:Large Label Last 策略,设队首元素为i,队列中所有dist值的平均值为x,若dist(i)>x则将i插入
到队尾,查找下一元素,直到找到某一i使得dist(i)<=x,则将i出对进行松弛操作。
引用网上资料,SLF 可使速度提高 15 ~ 20%;SLF + LLL 可提高约 50%。
在实际的应用中SPFA的算法时间效率不是很稳定,为了避免最坏情况的出现,通常使用效率更加稳定的Dijkstra算法。
*/
 
//用数组实现邻接表存储,pnt[i,0]表示与i相邻的结点个数,pnt[i,1...k]存储与i相邻的点

int SPFA(){
    for(int i = 1; i <= n; i++){
        head[i] = 0;	
        dst[i] = INF;
        vis[i] = false;
        slack[i] = 0;
    }
	while(que.empty() == false)que.pop();
	que.push(1);
	dst[1] = 0;
	vis[1] = true;
	while(que.empty() == false){
		int idx = que.front();que.pop();
		vis[idx] = false;
		for(int i = head[idx]; i != 0; i = e[i].nxt){
			int to = e[i].to;
			int d = dst[idx] + e[i].w;
			if(d < dst[to]){
				dst[to] = d;
				slack[to] = slack[idx] + 1;
				if(slack[to] >= n){
					printf("YES\n");
					return 1;
				}
				
				if(vis[to])continue;
				vis[to] = true;
				que.push(to);
			}
		}
	}
	printf("NO\n");
	return 0;
}

FLOYD

初始大家都是邻接矩阵,然后每次扩展一个点,将与该点相连的点连通,并且求最短路径

void floyd(int idx){
	for(int k = 0; k < n; k++)
	for(int i = 0; i <n; i++){
		for(int j = i + 1; j < n; j++){
			adj[j][i] = adj[i][j] = min(adj[i][idx] + adj[idx][j], adj[i][j]);
		}
	}
}