## $spfa$和$bfs$的区别 $spfa$在形式上和$bfs$非常类似,不同的是$bfs$中一个点出了队列就不可能重新进入队列,但是$spfa$中一个点可能在出队列之后再次被放入队列,也就是一个点松弛过其它的点之后,过了一段时间可能本身被松弛,于是要再次用来松弛其它的点,这样反复迭代下去。 ### 一、$bfs$算法模板 ```c++ queue q; st[1] = true; // 表示1号点已经被遍历过 q.push(1); while (q.size()){ int u = q.front(); q.pop(); for (int i = h[u]; ~i; i = ne[i]){ int j = e[i]; if (!st[j]){ st[j] = true; // 表示点j已经被遍历过 q.push(j); } } } ``` ### 二、$spfa$算法模板 ```c++ int spfa(){ memset(dist, 0x3f, sizeof dist); dist[1] = 0; queue q; q.push(1); st[1] = true; while (q.size()){ auto u = q.front(); q.pop(); st[u] = false; for (int i = h[u]; ~i; i = ne[i]){ int j = e[i]; if (dist[j] > dist[u] + w[i]){ dist[j] = dist[u] + w[i]; // 如果队列中已存在j,则不需要将j重复插入 if (!st[j]){ q.push(j); st[j] = true; } } } } if (dist[n] == 0x3f3f3f3f) return -1; return dist[n]; } ``` 在$spfa$中一个点的距离 **可能被多次更新**,因为有权值为 **负数** 的边,而$Dijktra$,$bfs$所有边的 **权值都非负**,且每次更新其他点的距离的时候,**都取的是当前未确定和起点距离的点中距离最小的点**,所以当有其他路径通向这个点的时候不管是一条还是几条边距离一定都大于你选的这个边的距离,所以此时这个点到起点的距离就已经被确定不会改变了,而$spfa$允许存在负边,可能存在一条路径第一段权值较大,但是后几段距离是负数,就会更新$dist$了,所以在$spfa$算法中,在出队后将$st[t]$标记为$false$,使得以后还可以入队来更新路径。