由于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);
}
}