图论中比较基础的问题,求单源最短路,即在图中找一个点作为起点,求他到其他点的最短路,而Bellman-Ford算法是其中最简单的算法,相应地,其复杂度也比较高,效率也比较低,但是,他却可以判断图中是否存在负权回路(走一圈经过的权值是负数),因此可以处理带有负权边的图,且该算法是其他各种最短路算法的原始型,应当受到足够的重视。
设有图G< V, E >,点数为V,边数为E,源点为s点。我们用一个distance数组(下写为dis数组)来记录各点到s点的最短距离,将其初始化为INF(一般是个很大的常数),dis[s] = 0。
算法的基本思路是一种动态逼近的思想:
很显然,如果我们知道一条边起点到源点的最短距离和这条边的边权,那么终点到s点的最短距离为起点到源点的最短距离+边权,即 dis[终点] = dis[起点]+边权。
据此,我们执行V-1次对每一条边的松弛操作:对于该边,如果dis[起点]+边权 < dis[终点],则将dis[终点]变为dis[起点]+边权。
为什么是V-1次呢?
假如是第一次对所有边进行松弛操作,dis一开始被初始化为INF,所以那些s点直接相连的点,他们的dis会更新,他们与s点之间的边的松弛是有效的;
而其他的点,他们不与s点直接相连,那么对于所有与这些点有关的边,他们起点的初始dis值与终点的初始dis值均为INF,所以松弛条件不满足,无法松弛。
第二次时,所有与源点直接相连的点的dis值都是有效的,按第一次同样的原理,所有与这些点直接相连的点的dis值被有效更新,dis值有效的范围在经过两次对所有边的松弛之后,以源点为根树状地扩展了两层,所有与s点小于等于两条边相连的点的dis值被有效更新。
于是我们知道,在G图中,一个点最坏情况和s点之间有V-1条边,所以至多我们进行V-1此对所有边的松弛操作即可得出图中任何一点到s点的最短路。
如果有边,其边权为负值,那么其dis[起点]+边权 恒小于 dis[终点],则对其的松弛永远都能进行。所以我们可以再进行一次(即第V次)对所有边的松弛操作,边集中若无负边权,则各点dis值均为最短路,无法满足松弛条件,若存在负边权,则此边松弛条件依然是满足的,可据此判断出存在负边权。
伪代码过程:
声明图G <V, E>, 数组dis[], 源点s;
for(i = 1 to V) dis[i] = INF;
dis[s] = 0;
for(i = 1 to V-1)
foreach 边∈G
if(dis[起点]+边权 < dis[终点])
dis[终点] = dis[起点] + 边权;
声明 flag = true; //若flag为false代表G中存在负边权
foreach 边∈G
if(dis[起点]+边权 < dis[终点])
flag = false;
最终dis[n]为n点到s点的最短路距离。