zoukankan      html  css  js  c++  java
  • 图论:Johnson全源最短路

    背景:

    Johnson 和 Floyd 一样,是一种能求出无负环图上任意两点间最短路径的算法。该算法在 1977 年由 Donald B. Johnson 提出。
    任意两点间的最短路可以通过枚举起点,跑 n次 Bellman-Ford 算法解决,时间复杂度是 O((n^2m))的,也可以直接用 Floyd 算法解决,时间复杂度为 O((n^3))。

    注意到堆优化的 Dijkstra 算法求单源最短路径的时间复杂度比 Bellman-Ford 更优,如果枚举起点,跑 n 次 Dijkstra 堆优化算法,就可以在 O((nmlogm))的时间复杂度内解决本问题,
    比上述跑 n 次 Bellman-Ford 算法的时间复杂度更优秀,在稀疏图上也比 Floyd 算法的时间复杂度更加优秀。

    但 Dijkstra 算法不能正确求解带负权边的最短路,因此我们需要对原图上的边进行预处理,确保所有边的边权均非负。

    P5905 【模板】Johnson 全源最短路

    点击查看代码块
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    typedef pair<ll,int> pii;
    
    const int inf = 0x7f7f7f7f;
    const int maxn = 1e4+10;
    
    int head[maxn],cnt=0;
    struct edge{
        int v,next;
        ll w;
    }e[maxn<<1];
    
    void add(int u,int v,ll w){
        e[cnt].v=v;e[cnt].w=w;e[cnt].next=head[u];head[u]=cnt++;
    }
    int n,m,S;//S为超级源点
    ll h[maxn];//spfa先跑一次出来超级源点到所有点的单源最短路
    int num[maxn];
    bool vis[maxn];
    
    bool spfa(int s){
        memset(num,0,sizeof(num));
        memset(vis,0,sizeof(vis));
        for (int i=0;i<=n;i++) h[i] = inf;
        h[s] = 0;
        vis[s] = 1;
        num[s] = 1;
        queue<int> q;
        q.push(s);
    
        while(!q.empty()){
            int u=q.front();
            q.pop();
            vis[u] = 0;
            for (int i=head[u];~i;i=e[i].next){
                int v=e[i].v;
                ll w=e[i].w;
                if(h[v] > h[u] + w){
                    h[v] = h[u] + w;
                    if(!vis[v]){
                        vis[v] = 1;
                        num[v]++;
                        q.push(v);
                        if(num[v]>n) return 1;
                    }
                }
            }
        }
        return 0;
    }
    
    ll dis[maxn];
    
    void dijkstra(int s){
        for (int i=1;i<=n;i++) dis[i] = inf;
        memset(vis,0,sizeof(vis));
        dis[s] = 0;
        priority_queue<pii,vector<pii>,greater<pii> >q;
        q.push({dis[s],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].v;
                ll w=e[i].w;
                if(dis[v]>dis[u]+w){
                    dis[v]=dis[u]+w;
                    q.push({dis[v],v});
                }
            }
        }
    }
    
    int main(){
        memset(head,-1,sizeof(head));
        cnt = 0;
        cin>>n>>m;
        for (int i=1;i<=m;i++){
            int u,v;
            ll w;
            scanf("%d%d%lld",&u,&v,&w);
            add(u,v,w);
        }
        S = 0;
        for (int i=1;i<=n;i++){//超级源点和所有点连一条边权为0的边
            add(S,i,0);
        }
        if(spfa(S)){
            puts("-1");return 0;
        }
        //更改边权为w+h[u]-h[v]
        for (int u=1;u<=n;u++){
            for (int i=head[u];~i;i=e[i].next){
                int v=e[i].v;
                ll w=e[i].w;
                e[i].w = w + h[u] - h[v];
            }
        }
        //对每个点再跑一次dijkstra
        for (int i=1;i<=n;i++){
            dijkstra(i);
            ll ans = 0;
            for (int j=1;j<=n;j++){
                if(dis[j] == inf) ans += 1ll*j*1e9;
                else ans += 1ll*j*(dis[j]+h[j]-h[i]);//这里注意:原本加上来的现在要减回去
            }
            printf("%lld
    ",ans);
        }
        return 0;
    }
    /*
    3 2
    1 2 -2
    1 3 -3
    */
    
    你将不再是道具,而是成为人如其名的人
  • 相关阅读:
    html集合
    pyautocad
    CAD 批量提取点坐标,实现坐标的快速提取
    CAD
    python模块
    set,get,setter
    1 Http的表皮
    (6)小项目------完善增删改查的操作
    SSM学习笔记(6)---拦截器
    SSM学习笔记(5)-CGLIB动态代理
  • 原文地址:https://www.cnblogs.com/wsl-lld/p/14017933.html
Copyright © 2011-2022 走看看