JOHNSON全源最短路

由于dijkstra不能用于带有负边权的图,而Floyd算法的复杂度又过高,在处理稀疏图时往往耗时较久。

解决的方案是,使在有负边而无负环的情况下,依然可以使用dijkstra算法。

所谓的Johnson算法,其实就是在执行dijkstra算法之前,先执行一边Bellman-ford算法,通过外加一个点,给所有的原点赋一个相对值,这样既可以使负边消失,又如同重力势能一样,只改变相对值,可以在最后计算的时候复原。

需要注意的点:

1.为了使速度更快,我们使用Bellman-ford算法的堆优化版本,即SPFA算法。在判断是否存在负环的同时就能计算出每一点的相对值

2.添加的这个点,将和每一个原点之间连接一条边权为0的有向边,保证了图是连通的,同时,在判断负环时,判断条件所需要的n也要变成n+1看待

给出模板

#include <bits/stdc++.h>
#define maxn 3005
using namespace std;
inline int read() {
    char ch = getchar(); int x = 0, f = 1;
    while(ch < '0' || ch > '9')
    {
        if(ch == '-') f = -1;
        ch = getchar();
    } 
    while('0' <= ch && ch <= '9')
    {
        x = x * 10 + ch - '0';
        ch = getchar();
    } return x * f;
}
typedef pair<long long,int>P;
int n,m;
struct edge
{
    int to;
    long long cost;
};
vector<edge>Q[maxn];
priority_queue<P,vector<P>, greater<P> >G;
long long d[maxn],h[maxn];
queue<int>SF;
int cnt[maxn],v[maxn];
void pre(int s)
{
    fill(d,d+maxn,1000000000);
    d[s]=0;
    G.push(P(0,s));
    while(!G.empty())
    {
        P e=G.top();G.pop();
        int h=e.second;
        if(d[h]<e.first)continue;
        for(int j=0;j<Q[h].size();j++)
        {
            edge y=Q[h][j];
            if(d[y.to]>d[h]+y.cost)
            {
                d[y.to]=d[h]+y.cost;
                G.push(P(d[y.to],y.to));
            }
        }
    }
}
bool check(int k)
{
    fill(h,h+maxn,100000000000000000ll);
    h[k]=0;
    SF.push(k);
    v[k]=1;
    while(!SF.empty())
    {
        int x=SF.front();
        SF.pop();
        v[x]=0;
        for(int i=0;i<Q[x].size();i++)
        {
            int y=Q[x][i].to;
            if(h[y]>h[x]+Q[x][i].cost)
            {
                h[y]=h[x]+Q[x][i].cost;
                cnt[y]=cnt[x]+1;
                if(cnt[y]>n)return false;
                if(!v[y])SF.push(y),v[y]=1;
            }
        }
    }
    return true;
}
int main()
{
    int k;
    n=read(),m=read();
    int u,v;
    long long w;
    for(int i=0;i<m;i++)
    {
        u=read(),v=read(),w=read();
        Q[u].push_back((edge){v,w});
    }
    for(int i=1;i<=n;i++)Q[0].push_back((edge){i,0});
    if(!check(0))
    {
        printf("-1");
        return 0;
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<Q[i].size();j++)
        {
            Q[i][j].cost+=(h[i]-h[Q[i][j].to]);
        }
    }
    for(int i=1;i<=n;i++)
    {
        pre(i);
        long long ans=0;
        for(int j=1;j<=n;j++)
        {

            if(d[j]==1000000000)ans=ans+j*d[j];
            else ans=ans+j*(d[j]+h[j]-h[i]);
        }
        printf("%lld\n",ans);
    }
}
# P5905 【模板】全源短路Johnson) ## 题目描述 给定一个包含 $n$ 个结点和 $m$ 条带权边的有向图,求所有点对间的短路径长度,一条路径的长度定义为这条路径上所有边的权值和。 注意: 1. 边权**可能**为负,且图中**可能**存在重边和自环; 2. 部分数据卡 $n$ 轮 SPFA 算法。 ## 输入格式 第 $1$ 行:$2$ 个整数 $n,m$,表示给定有向图的结点数量和有向边数量。 接下来 $m$ 行:每行 $3$ 个整数 $u,v,w$,表示有一条权值为 $w$ 的有向边从编号为 $u$ 的结点连向编号为 $v$ 的结点。 ## 输出格式 若图中存在负环,输出仅一行 $-1$。 若图中不存在负环: 输出 $n$ 行:令 $dis_{i,j}$ 为从 $i$ 到 $j$ 的短路,在第 $i$ 行输出 $\sum\limits_{j=1}^n j\times dis_{i,j}$,注意这个结果可能超过 int 存储范围。 如果不存在从 $i$ 到 $j$ 的路径,则 $dis_{i,j}=10^9$;如果 $i=j$,则 $dis_{i,j}=0$。 ## 输入输出样例 #1 ### 输入 #1 ``` 5 7 1 2 4 1 4 10 2 3 7 4 5 3 4 2 -2 3 4 -3 5 3 4 ``` ### 输出 #1 ``` 128 1000000072 999999978 1000000026 1000000014 ``` ## 输入输出样例 #2 ### 输入 #2 ``` 5 5 1 2 4 3 4 9 3 4 -3 4 5 3 5 3 -2 ``` ### 输出 #2 ``` -1 ``` ## 说明/提示 【样例解释】 左图为样例 $1$ 给出的有向图,短路构成的答案矩阵为: ``` 0 4 11 8 11 1000000000 0 7 4 7 1000000000 -5 0 -3 0 1000000000 -2 5 0 3 1000000000 -1 4 1 0 ``` 右图为样例 $2$ 给出的有向图,红色标注的边构成了负环,注意给出的图不一定连通。 ![](https://cdn.luogu.com.cn/upload/image_hosting/7lb35u4u.png) 【数据范围】 对于 $100\%$ 的数据,$1\leq n\leq 3\times 10^3,\ \ 1\leq m\leq 6\times 10^3,\ \ 1\leq u,v\leq n,\ \ -3\times 10^5\leq w\leq 3\times 10^5$。 对于 $20\%$ 的数据,$1\leq n\leq 100$,不存在负环(可用于验证 Floyd 正确性) 对于另外 $20\%$ 的数据,$w\ge 0$(可用于验证 Dijkstra 正确性) upd. 添加一组 Hack 数据:针对 SPFA 的 SLF 优化
最新发布
07-18
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值