Bellman-Ford 算法

Bellman-Ford算法:
D i j k s t r a Dijkstra Dijkstra类似,都以松弛操作为基础,即估计的最短路径值渐渐地被更加准确的值替代,直至得到最优解。在两个算法中,计算时每个边之间的估计距离值都比真实值大,并且被新找到路径的最小长度替代。

松弛

每次松弛操作实际上是对相邻节点的访问,第 n次松弛操作保证了所有深度为n的路径最短。由于图的最短路径最长不会经过超过 |V|-1条边。

负边权操作

“松弛”操作是在广度上找路,所以对负边进行操作而不会影响结果。

负权环判定

在处理完所有的边后,如果还有边可以松弛,那么就一定存在负权环。

朴素实现

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int MAXN = 505;

struct Node {
    int u, v, w;
} node[MAXN];

int n, m, s, t;
int dis[MAXN], pre[MAXN];

bool Bellman_Ford(int s, int t) {
    memset(dis, 0x3f, sizeof(dis));
    dis[s] = 0;
    pre[s] = 0;
    for (int i = 1; i < n; i++) {//迭代 n - 1次
        for (int j = 1; j <= m; j++) {//遍历每一条边
            if (dis[node[j].u] + node[j].w < dis[node[j].v]) {//松弛
                dis[node[j].v] = dis[node[j].u] + node[j].w;
                pre[node[j].v] = node[j].u;
            }
        }
    }
    for (int i = 1; i <= m; i++) {//判断负权环
        if (dis[node[i].u] + node[i].w < dis[node[i].v]) {
            return false;
        }
    }
    return true;
}

void print(int x) {//输出路径
    if (pre[x] == 0) {
        printf("%d ", x);
        return;
    }
    print(pre[x]);
    printf("%d ", x);
}

int main() {
    scanf("%d %d", &n, &m);
    for (int i = 1, ui, vi, wi; i <= m; i++) {
        scanf("%d %d %d", &ui, &vi, &wi);
        node[i].u = ui;
        node[i].v = vi;
        node[i].w = wi;
        pre[vi] = ui;
    }
    scanf("%d %d", &s, &t);
    if (Bellman_Ford(s, t)) {
        printf("%d\n", dis[t]);
        print(t);
    } else {
        printf("No Solution\n");
    }
    return 0;
}

Spfa

思想

建一个队列 ,取队头顶点u;将与点u相连的所有点v松弛,如果能更新估计值,那么就更新,如果点v没有在队列中,那么要将点v入队,如果已经在队列中了,那么就不用,循环直到队空为止,完成了单源最短路的求解。

实现


int n, m, s, t;
int dis[MAXN];
bool vis[MAXN];

struct edge {//存边
    int v, w;
    edge() {}
    edge(int V, int W) {
        v = V;
        w = W;
    }
};

vector<edge> G[MAXM];
queue<int> q;

void AddEdge(int u, int v, int w) {//加边
    G[u].push_back(edge(v, w));
    G[v].push_back(edge(u, w));
}

void Spfa(int s, int t) {
    memset(dis, 0x3f, sizeof(dis));
    memset(vis, 0, sizeof(vis));
    dis[s] = 0;
    vis[s] = 1;
    q.push(s);
    while (q.size()) {
        int u = q.front();//取出队头
        q.pop();
        vis[u] = 0;
        for (int i = 0; i < G[u].size(); i++) {
            if (dis[u] + G[u][i].w < dis[G[u][i].v]) {//扩展新节点
                dis[G[u][i].v] = dis[u] + G[u][i].w;
                if (!vis[G[u][i].v]) {
                    vis[G[u][i].v] = 1;
                    q.push(G[u][i].v);
                }
            }
        }
    }
}