Bellman-Ford

Bellman-Ford

Bellman-Ford 算法的证明

Bellman-Ford 算法各方面都没有 SPFA 算法优良,但是有些题只能用 Bellman-Ford 算法来求,SPFA 算法要求图中不存在负环。不过,只要图中没有负环,都可以使用 SPFA,绝大部分算法题求最短路都没负环。

Bellman-Ford 算法对边的存储不太敏感,你甚至可以不使用邻接表或邻接矩阵存边,直接使用一个结构体一维数组也行。

struct Edge {
	int a, b, w
}edges[M];

Bellman-Ford 算法模板

dist[i] 表示从 1 号点到 i 号点的最短距离。

memset(dist, 0x3f, sizeof dist);
dist[1] = 0; // 距离数组初始化操作

for n 次 {
    memcpy(backup, dist, sizeof dist); // 需要使用备份数组,防止发生“串联”
    for 所有边 a,b,w {
        dist[b] = min(dist[b], backup[a] + w); // 使用backup而不是dist,是因为dist[a]可能在第二个for循环中被提前更新了。我们应当使用第一个for循环的上一次迭代得到的dist[a]数组值
    }
}

外层 for 循环实际含义:循环到第 k 次时,表示从 1 号点,经过不超过 k 条边,到达所有点的最短距离(这个距离可能存在也可能不存在)。 对于外层 for 循环,每次迭代都会确定 一个 从起点到次点的最短路径。因此想要确定起点到 n 号点且不多于 k 条边的最短路径就是循环 k 次。

对于边 w(a, b),当 d[a] 第一次更新时,d[b] 才可能更新,在 d[a] 被更新之前,d[b] 的值一直是无穷大。 若图中存在未确认的顶点,则对边集合的一次迭代后,会增加至少一个已确认顶点

对于存在负权回路的最短路,答案可能不存在。

const int N = 510, M = 10010;
int n, m, k;

int d[N];
int backup[N];

struct Edge {
    int a, b, w;
}edges[M];

int bellman_ford() {
    memset(d, 0x3f, sizeof d); // 距离数组初始化为一个极大值
    d[1] = 0;
  
    while (k --) {
        memcpy(backup, d, sizeof d);

        for (int i = 1; i <= m; ++i) {
            Edge& now = edges[i];
            d[now.b] = min(d[now.b], backup[now.a] + now.w);
        }
    }

    if (d[n] >= 0x3f3f3f3f / 2) return -1;
    else return d[n];
}

Bellman-Frod 算法时间复杂度

O ( n m ) O(nm) O(nm),其中 n 表示图的点数,m 表示图的边数。

参考:AcWing

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值