Bellman-Ford算法

学SPFA已经有一段时间了,还没有学他最原始的算法版本,于是这里就补一个Bellman-Ford算法吧。
这是一个解决单源无负环最短路的算法

主要思想

  • 我们知道要让图联通,每一个点必须有一条边相连。
  • 我们每次取一个点取延申,不断地得到最优的路劲。同时如果没有负环存在的情况下,我们的最终拓展边数将会是n - 1。也就是遍历n - 1次边

例子

共有8条有向边

  • 1 2 2
  • 1 3 5
  • 2 3 4
  • 2 5 10
  • 3 4 2
  • 4 2 6
  • 4 6 1
  • 5 6 3

我们取点1作为出发边,求从点1到其他边的距离

有dis数组, dis[6] = {0, INF, INF, INF, INF, INF}

第一遍遍历, dis[6] = {0, 2, 5, INF, INF, INF}

第二遍遍历, dis[6] = {0, 2, 5, 7, 12, INF}

第三遍便利, dis[6] = {0, 2, 5, 7, 12, 8},这里得到了所有点到点1的最短路。而且遍历次数是小于n的。

我们假设一种最极端的例子,路径中只有一条路,也就是每个点都只连着一条路,这里的遍历次数是最多的要(n - 1)次

求最短路的关键代码

//Powered by CK 2020:04:06
int from[N], to[N], value[N], dis[N], n, m;//分别是出发点,终点,边权值,到点1的距离,点数,边数。
void Bellman_Ford() {
    for(int i = 1; i <= n; i++) dis[i] = INF;
    dis[1] = 0;
    while(true) {
        bool can_shorted = true;
        for(int i = 0; i < m; i++) {
            if(dis[to[i]] > dis[from[i]] + value[i]) {//遍历一遍下来可以使边缩小,标记can_shorted标记。
                dis[to[i]] = dis[from[i]] + value[i];
                can_shorted = false;
            }
        }
        if(can_shorted)     break;
    }
}

判断是否存在负边权的代码

//Powered by CK 2020:04:06
int from[N], to[N], value[N], dis[N], n, m;
bool Bellman_Ford() {
    for(int i = 1; i <= n; i++) dis[i] = INF;
    dis[1] = 0;
    for(int j = 0; j < n)
        for(int i = 0; i < m; i++) {
            if(dis[to[i]] > dis[from[i]] + value[i]) {
                dis[to[i]] = dis[from[i]] + value[i];
                if(j == n - 1)  return true;//上面已经说明的,如果不存在负边权,不可能可以到一个点两次。
            }
        }
    }
    return false;
}

复杂度分析

n * m, 但是通过上面的例子可以分析,到n * m一般不可能,真实的结果使比这个稍微小的。

posted @ 2020-04-06 21:00  lifehappy  阅读(607)  评论(0编辑  收藏  举报