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
    */
    
    你将不再是道具,而是成为人如其名的人
  • 相关阅读:
    Git 基础
    SharePoint 2013 对象模型操作"网站设置"菜单
    SharePoint 2013 隐藏部分Ribbon菜单
    SharePoint 2013 Designer系列之数据视图筛选
    SharePoint 2013 Designer系列之数据视图
    SharePoint 2013 Designer系列之自定义列表表单
    SharePoint 2013 设置自定义布局页
    SharePoint 2013 "通知我"功能简介
    SharePoint 2013 创建web应用程序报错"This page can’t be displayed"
    SharePoint 禁用本地回环的两个方法
  • 原文地址:https://www.cnblogs.com/wsl-lld/p/14017933.html
Copyright © 2011-2022 走看看