算法
优化核心思维:每次仅对发生变化了的点的相邻边执行松弛操作。但是如何知道当前哪些点的最短路径发生了变化呢?这里可以用一个队列来维护这些点,算法大致如下:
每次选取队首顶点u,对顶点u的所有边进行松弛操作。例如有一条u->v的边,如果通过u->v这条边使得源点到顶点v的最短路程变短(dis[u] + e[u][v] < dis[v]),且顶点v不在当前的队列中,我们就将顶点v放入队尾。需要注意的是,同一个顶点同时在队列中出现多次是毫无意义的,所以我们需要一个数组来判重(判断哪些点已经在队列中)。在对顶点u的所有出边松弛完毕后,就将顶点u出队。接下来中取出新的队首再进行如上操作,直至队列为空。
下面我们看一个具体的例子。
5 7
1 2 2
1 5 10
2 3 3
2 5 7
3 4 4
4 5 5
5 3 6
第一行两个整数n,m。n表示顶点个数(顶点编号为1~N),m表示边的条数。接下来m行,每行有3个数x y z。表示顶点x到顶点y的边的权值为z。
我们用数组dis来存放1号顶点到其余各个顶点的最短路径。初始时dis[1]为0,其余为无穷大,接下来将1号顶点入队。队列这里用一个数组que以及两个分别指向队列头和尾的变量head和tail来实现。
先来看当前队首1号顶点的边1->2,先来看通过1->2能否让1号顶点到2号顶点的路程(即dis[2])变短,也就是说先来比较dis[2]和dis[1] + (1->2)的大小。dis[2]原来的值为inf,dis[1] + (1->2)的值为2,因此松弛成功,dis[2]的值从inf更新为2。并且当前2号顶点不再队列中,因此将2号顶点入队。
同样,对1号顶点剩余的出边进行如上操作,处理完毕后数组dis和队列que状态如下:
对1号顶点处理完毕后,就将1号顶点出队列(head++即可),再对新队首2号顶点进行如上处理。在处理2->5这条边的时候需要特别注意一下,2->5这条边虽然可以让1号顶点到5号顶点的路程变短(dis[5]的值从10更新到9),但是5号顶点已经在队列中了,因此5号顶点不能再次入队。对2号顶点处理完毕后数组dis和队列que状态如下:
在对2号顶点处理完毕后,需要将2号顶点出队,并依次对剩下的顶点做相同的处理,直至队列为空为止。最终数组dis和队列que的状态如下:
代码实现
我们还是用邻接表来储存这个图,具体代码如下:
//优化后的Bellman-Ford算法.2
#include <bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
int main