Bellman-Ford算法
算法核心代码
for(int i=0;i<n-1;i++)//如果有n个定点 那么最多需要n-1次松弛(下面会有证明)
{
for(int j=0;j<m;j++)//更新每一条边
{
int x=u[j];int y=v[j];
//如果源点可以和这条边的定点联通
// 那么代表这条边可以进行比较更新但并不一定
//会改变的这个值
if(d[x]<inf)
{
d[y]=min(d[y],d[x]+w[j]);
}
}
}
为什么最多需要n-1次的松弛操作呢
首先对这个算法进行解释一下 :这个算法每次检查所有的边 如果发现这条边的起点和源点有一条通路
那么我们进行一次松弛尝试 每一次松弛实际的操作是 对已经与源点连同的点为起点的边 来 松弛源点到这条边的终点
那么接着上面 设v(s,j)为s到j的一条通路 s(s,j)代表s->j 中所经过的点的集合 取 max(s(i,j))=m
如果要用 这条边 来更新s->j的距离 那么则需要最多m-1 次松弛 因为这中间还和边的遍历顺序有关 如果边的遍历顺序恰好和更新顺序吻合 那么一次循环就解决了这个最长路 但是如果恰好相反则就会增加 而且最多会是m-1 下面的证明是以最糟糕的情况出发 因为我们的算法必须解决所有出现的情况 所以以最坏的情况出发就变成了必然!
设这条具体路径为 s->j-m+1->j-m+2…j-1->j 这条路最后一次松弛必须为 j-1->j
为什么最后一次松弛必须为j-1->j这条边呢
我们用反证法来证明 假设最后一次松弛是j-1->前面的k边然后完成了s->j距离的更新 那么 更新完 这条边后并不是s->j的距离 而是s->( k边终点 )所以与题设矛盾
所以松弛的最后一条边是j-1->j 同理 要更新 j-1->j 这条边需要他前面的那条边更新完成 所以以此类推 一共进行了m-1次更新 完成了s->j这条最长边的松弛
而进一步 我们知道m<=n 所以最大为n 所以 松弛最多进行n-1轮
所以可得 时间复杂度为 o(m*n)
对于这个算法 可以解决负的环的问题 是不对的 通过上述代码 如果出现了负环 还是无法出现最短路径
因为在每一次对边的松弛都会 使到这个点的距离更短 但是 我们可以发现是否存在一个负环 我们在更新完距离之后 在增加一次松弛 如果距离还能缩小 说明存在负环 。
bool flag=true;
for(int i=0;i<n;i++)
{
int x=u[i];int y=v[i];
if(d[x]<inf&&d[y]>d[x]+w[i])
{
flag=false;
}
}