• 【图论】Johnson算法


    适用于求解没有负环的全源最短路,最坏时间复杂度 (O(nmlog m)) 比Floyd要优秀(但是Floyd可以找出负环)。
    在没有负权边时,使用n次单源最短路Dijkstra代替即可。

    算法流程:

    1、新建一个虚拟节点(编号为n+1),向[1,n]连接一条边权为0的虚拟边。
    2、从n+1号节点开始跑一次队列优化BellmanFord,得到从n+1号虚拟节点到节点i的最短路hgt[i],假如存在负环,则无法求解。
    3、删除虚拟节点和虚拟边(也可以直接选择忽视他们)
    4、把每条非虚拟边(u,v,w)改为(u,v,w+hgt[u]-hgt[v])
    5、对于每个点i属于[1,n],跑一次Dijkstra,得到从i节点到[1,n]的非虚拟节点的最短路dis[j]。
    6、原本的[i,j]的最短路为dis[j]+hgt[i]-hgt[j]

    最后把边权修改回来的操作可以省去。

    namespace Johnson {
    
        const int MAXN = 2e5 + 10;
    
        int n;
        vector<pil> G[MAXN];
    
        ll hgt[MAXN];
        ll dis[MAXN];
        int cnt[MAXN];
        bool vis[MAXN];
    
        queue<int> Q;
        priority_queue<pli> PQ;
    
        bool bellmanford() {
            fill(hgt + 1, hgt + 1 + n, LINF);
            fill(cnt + 1, cnt + 1 + n, 0);
            fill(vis + 1, vis + 1 + n, 0);
            while(!Q.empty())
                Q.pop();
            hgt[n] = 0;
            vis[n] = 1;
            Q.push(n);
            while(!Q.empty()) {
                int u = Q.front();
                Q.pop();
                vis[u] = 0;
                ++cnt[u];
                if(cnt[u] == n)
                    return false;
                for(pil &p : G[u]) {
                    int v = p.first;
                    ll w = p.second;
                    if(hgt[v] <= hgt[u] + w)
                        continue;
                    hgt[v] = hgt[u] + w;
                    if(!vis[v]) {
                        vis[v] = 1;
                        Q.push(v);
                    }
                }
            }
            return true;
        }
    
        void dijkstra(int s) {
            fill(dis + 1, dis + 1 + n, LINF);
            fill(vis + 1, vis + 1 + n, 0);
            while(!PQ.empty())
                PQ.pop();
            dis[s] = 0;
            PQ.push({-dis[s], s});
            while(!PQ.empty()) {
                int u = PQ.top().second;
                PQ.pop();
                if(vis[u])
                    continue;
                vis[u] = 1;
                for(pil &p : G[u]) {
                    int v = p.first;
                    ll w = p.second;
                    if(vis[v] || dis[v] <= dis[u] + w)
                        continue;
                    dis[v] = dis[u] + w;
                    PQ.push({-dis[v], v});
                }
            }
        }
    
        bool johnson() {
            ++n;
            G[n].clear();
            for(int i = 1; i <= n - 1; ++i)
                G[n].eb(i, 0);
            int res = bellmanford();
            G[n].clear();
            --n;
            if(!res)
                return false;
            for(int u = 1; u <= n; ++u) {
                for(pil &p : G[u]) {
                    int v = p.first;
                    p.second += hgt[u] - hgt[v];
                }
            }
            for(int i = 1; i <= n; ++i) {
                dijkstra(i);
                for(int j = 1; j <= n; ++j) {
                    if(dis[j] != LINF)
                        dis[j] -= hgt[i] - hgt[j];
                }
                // do something
            }
            for(int u = 1; u <= n; ++u) {
                for(pil &p : G[u]) {
                    int v = p.first;
                    p.second -= hgt[u] - hgt[v];
                }
            }
            return true;
        }
    
    }
    
  • 相关阅读:
    oracle lpad函数和rpad函数
    OREACLE SUBSTR()函数应用-截取字符函数
    oracle常用数值函数
    Oracle 分析函数row_number() over (partition by order by )
    oracle中decode函数用法
    oracle数据字典信息整理
    python学习遇到的英文词汇
    读书随想
    常用css列表
    爬虫趣事
  • 原文地址:https://www.cnblogs.com/purinliang/p/14394864.html
Copyright © 2020-2023  润新知