Bellman-Ford算法和队列优化(SPFA)——求单源最短路径

#include <stdio.h>
#include <limits.h>
int main(){
    int n, m;
    scanf("%d%d", &n, &m);

    int u[10], v[10], w[10];    // 分别表示边的两顶点编号和权值
    for (int i = 1; i <= m; i++){
        scanf("%d%d%d", &u[i], &v[i], &w[i]);
    }

    int dis[10];    // 源点到任意点的距离
    for (int i = 1; i <= n; i++){
        dis[i] = INT_MAX;
    }
    dis[1] = 0;

    int bak[i];
    // 核心语句,循环n-1次
    for (int k = 1; k <= n - 1; k++){

        // 备份距离数组以检测是否已完成
        for (int i = 1; i <= n; i++) bak[i] = dis[i];

        // 每次循环遍历所有边
        for (int i = 1; i <= m; i++){
            // 判断源点到点v[i]距离能不能通过走源点->点u[i]->点v[i]的路线更短
            if (dis[v[i]] > dis[u[i]] + w[i]){
                dis[v[i]] = dis[u[i]] + w[i];
            }
        }

        // 循环完检测当前dis是否有更新
        bool check = 0;
        for (int i = 1; i <= n; i++) {
            if (bak[i] != dis[i]) {
                check = 1;
                break;
            }
        }
        if (check = 0) break;   // 如果 dis数组没更新,提前退出循环
    }

    // 检测负权回路
    bool flag = 0;
    for (int i = 1; i <= m; i++){
        if (dis[v[i]] > dis[u[i]] + w[i]) flag = 1;
    }
    if (flag == 1) printf("有负权回路\n");

    // 输出最终结果
    for (int i = 1; i <= n; i++){
        printf("%d ", dis[i]);
    }
    return 0;
}
// 队列优化的Bellman-Ford算法(SPFA算法)
#include <iostream>
#include <climits>
#include <queue>
#include <vector>
using namespace std;

int main(){
    int n, m;
    cin >> n >> m;

    vector <int> dis(n, INT_MAX);   // 源点到每个点的距离
    dis[0] = 0;

    vector <int> first(n, -1);  // 每个结点相邻的边中第一条边的编号,-1表示没有相邻的边
    vector <int> next(m, -1);   // 每条边的下一条边的编号

    vector <bool> book(n, false);   // true表示在队列中

    vector <int> u(m), v(m), w(m);  // 每条边的两顶点和权值

    // 建立邻接表
    for (int i = 0; i < m; i++){
        cin >> u[i] >> v[i] >> w[i];
        // 将新边头插到邻接表中
        next[i] = first[u[i]];  // 此边的下一边为u[i]起头的第一条边
        first[u[i]] = i;    // 将此边作为u[i]起头的第一条边
    }

    queue <int> Q;
    // 将源点入队列
    Q.push(0);
    book[0] = true;

    // 队列不空时循环
    while (!Q.empty()){
        // 取队首
        int k = first[Q.front()];   // k表示队首点的第一条邻接边的编号
        // 扫描队首顶点所有的邻边
        while (k != -1){
            // 判断源点到点v[k]的距离能不能通过源点->点u[k]->点v[k]来更新
            if (dis[v[k]] > dis[u[k]] + w[k]){
                dis[v[k]] = dis[u[k]] + w[k];

                // 判断点v[k]在不在队列中
                if (book[v[k]] == false){
                    // 不在队列中则入队
                    Q.push(v[k]);
                    book[v[k]] = true;
                }
            }
            // k移向下一条邻接边
            k = next[k];
        }
        // 队首结点用完了,可以出队了
        book[Q.front()] = false;
        Q.pop();
    }

    // 输出源点到其余各个顶点的最短路径
    for (int i = 0; i < n; i++){
        cout << dis[i] << " ";
    }
    cout << endl;

    return 0;
}

发表评论