• Johnson 算法求全源最短路学习笔记


    Johnson 算法求全源最短路学习笔记

    Johnson 和 Floyd 一样,是一种能求出无负环图上任意两点间最短路径的算法。该算法在 1977 年由 Donald B. Johnson 提出。

    算法概述

    任意两点最短路可以用 Floyd 算法解决,时间复杂度 (O(n ^ 3))

    在没有负权边时,可以跑 (n) 次 Dijkstra,时间复杂度 (O(nm log_2 n)),在稀疏图上会比较优秀;

    Johnson 算法通过一些方法使有负权边的图也适用于第二种做法。

    详细步骤

    先把所有点加入队列,用 spfa 进行松弛,每个点上的顶标记为 (h_u)

    再将边权设为 (w'(u,v) = w(u,v) + h_u - h_v)

    (n) 次 Dijkstra,此时新图上两点间的最短路 (dis'(u,v)) 和原图上最短路 (dis(u,v)) 满足 (dis(u,v) = dis'(u,v) - (h_u - h_v))

    时间复杂度 (O(nm log_2 n))

    正确性证明

    证明将分为两部分:

    1. (dis(u,v) = dis'(u,v) - (h_u - h_v))
    2. 新图上没有负权边。

    ((1))

    (p_1, p_k) 间的一条路径为 (p_1, p_2 , p_3 dots , p_{k - 1}, p_k)

    则有:

    [sum_{i = 1}^{k - 1} w'(p_i, p_{i + 1}) = sum_{i = 1} ^ {k - 1} w(p_i, p_{i + 1}) + h_{p_i} - h_{p_{i + 1}} = h_{p_1} - h_{p_k} + sum_{i = 1}^{k - 1} w(p_i, p_{i + 1}) ]

    ((2))

    (h_u) 顶标是用 spfa 松弛过的,所以满足三角形不等式:

    对于一条边 ((u,v)),一定有 (h_u + w(u,v) geq h_v) ,即:(w(u,v) + h_u - h_v geq 0)

    (Source)

    不一定是对的,没有例题,也没有拍过

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <queue>
    int in() {
        int x = 0; char c = getchar(); bool f = 0;
        while (c < '0' || c > '9')
            f |= c == '-', c = getchar();
        while (c >= '0' && c <= '9')
            x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
        return f ? -x : x;
    }
    template<typename T>inline void chk_min(T &_, T __) { _ = _ < __ ? _ : __; }
    template<typename T>inline void chk_max(T &_, T __) { _ = _ > __ ? _ : __; }
    
    const int N = 1e3 + 5, mod = 998244353;
    
    inline void add(int _, int __) {
        _ += __;
        if (_ >= mod)
            _ -= mod;
        if (_ < 0)
            _ += mod;
    }
    
    struct edge {
        int next, to, w;
    } e[N << 1];
    int ecnt = 1, head[N];
    int n, m;
    int h[N], dis[N][N];
    
    inline void jb(const int u, const int v, const int w) {
        e[++ecnt] = (edge){head[u], v, w}, head[u] = ecnt;
    }
    
    void spfa() {
        static int vis[N];
        std::queue<int> q;
        for (int i = 1; i <= n; ++i)
            q.push(i), h[i] = 0, vis[i] = 1;
        while (!q.empty()) {
            int u = q.front(); q.pop();
            for (int i = head[u]; i; i = e[i].next) {
                int v = e[i].to;
                if (h[v] > h[u] + e[i].w) {
                    h[v] = h[u] + e[i].w;
                    if (!vis[v])
                        q.push(v), vis[v] = 1;
                }
            }
        }
    }
    
    void dijkstra(const int s, int *d) {
        static int vis[N];
        memset(vis, 0, sizeof(vis));
        std::priority_queue<std::pair<int, int> > q;
        memset(d, -1, sizeof(int) * (n + 1)); 
        q.push(std::make_pair(0, s));
        while (!q.empty()) {
            int u = q.top().second;
            q.pop();
            if (vis[u])
                continue;
            vis[u] = 1;
            for (int i = head[u]; i; i = e[i].next) {
                int v = e[i].to;
                if (d[v] > d[u] + e[i].w + h[u] - h[v]) {
                    d[v] = d[u] + e[i].w + h[u] - h[v];
                    q.push(std::make_pair(d[v], v));
                }
            }
        }
        for (int i = 0; i <= n; ++i)
            d[i] -= h[s] - h[i];
    }
    
    int main() {
        //freopen("in", "r", stdin);
        n = in(), m = in();
        while (m--) {
            int x = in(), y = in(), z = in();
            jb(x, y, z);
        }
        spfa();
        for (int i = 1; i <= n; ++i)
            dijkstra(i, dis[i]);
        int res = 0;
        for (int i = 1; i <= n; ++i)
            for (int j = 1; j <= n; ++j)
                add(res, dis[i][j]);
        printf("%d
    ", res);
        return 0;
    }
    
  • 相关阅读:
    【LGR-070】洛谷 3 月月赛-官方题解
    洛谷P1034 矩形覆盖
    <C和指针---读书笔记9>
    <C和指针---读书笔记8>
    <C和指针---读书笔记7>
    <C和指针---读书笔记6>
    <C和指针---读书笔记1>
    <C和指针---读书笔记2>
    <C和指针---读书笔记5>
    <C和指针---读书笔记4>
  • 原文地址:https://www.cnblogs.com/15owzLy1-yiylcy/p/11787586.html
Copyright © 2020-2023  润新知