全源最短路径求解其实是单源最短路径的推广,求解单源最短路径的两种算法时间复杂度分别为:
如果对全图顶点遍历,使用 Dijkstra 算法,时间复杂度将变成 \(O(VE + V2logV)\),看起来优于 Floyd-Warshall 算法的 \(O(V3)\)。不过,Dijkstra 算法要求权值重不能为负。
Johnson 算法能调整权重为负的图,使之能够使用 Dijkstra 算法。
以下图为例,Johnson 算法对下图进行re-weight操作,使权重不为负,并且re-weight后,计算出来的最短路径仍然正确。
首先,新增一个源顶点 ,并使其与所有顶点连通,新边赋权值为 0,如下图所示。
接下来重新计算新增顶点到其它顶点的最短路径,利用单源最短路径算法,图中存在负权重节点,使用bellman ford算法,计算新增节点到其它节点的最短路径 h[],然后使用如下公式对所有边的权值进行 "re-weight":
w(u, v) = w(u, v) + (h[u] - h[v]).
对于此公式的证明请参考算法导论一书。
现在除新增结点外,其它结点的相关边权重值都已经为正数了,可以将新增结点删除,对其它结点使用Dijkstra 算法了。
#include<bits/stdc++.h> using namespace std; #define int long long int read(){ int x=0;bool f=0;char ch=getchar(); while(!isdigit(ch))f|=ch=='-',ch=getchar(); while(isdigit(ch))x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return f?-x:x; } void write(int x){ if(x<0)putchar('-'),x=-x; if(x>9)write(x/10); putchar(48+x%10); } void writeln(int x){write(x);putchar('\n');} void writebl(int x){write(x);putchar(' ');} #define I inline #define R register const int maxn = 3e3+5; const int maxm = maxn*6; #define inf 1000000000 struct Johnson{ struct edge{int v,w,next;}e[maxm]; int head[maxn],vis[maxn],dis[maxn],tot,h[maxn]; void add(int u,int v,int w){e[++tot]=(edge){v,w,head[u]};head[u]=tot;} struct node{ int dis;int pos; bool operator < (const node &x)const{return x.dis<dis;} }; priority_queue<node> q; I void dijkstra(int s){ memset(dis,0x3f,sizeof(dis));memset(vis,0,sizeof(vis)); dis[s] = 0; q.push((node){0,s}); while(!q.empty()){ node tmp = q.top();q.pop(); int x = tmp.pos,d = tmp.dis; if(vis[x])continue;vis[x] = 1; for(int i = head[x];i;i = e[i].next){ int v = e[i].v; if(dis[v] > dis[x] + e[i].w){ dis[v] = dis[x] + e[i].w; if(!vis[v])q.push((node){dis[v],v}); } } } } int tim[maxn]; I bool spfa(int s,int n){ queue<int> q; memset(h,0x3f,sizeof(h));memset(vis,0,sizeof(vis)); h[s]=0;vis[s]=1;q.push(s); while(!q.empty()){ int u=q.front();q.pop();vis[u]=0; if(++tim[u]>n-1)return 0; for(R int i=head[u];i;i=e[i].next){ int v=e[i].v; if(h[v]>h[u]+e[i].w){ h[v]=h[u]+e[i].w; if(!vis[v])q.push(v),vis[v]=1; } } } return 1; } }J; int n,m; signed main(){ n=read(),m=read(); for(R int i=1,u,v,w;i<=m;++i){ u=read(),v=read(),w=read(); J.add(u,v,w); } for(R int i=1;i<=n;++i)J.add(0,i,0); if(!J.spfa(0,n)){puts("-1");return 0;} for(R int u=1;u<=n;++u) for(R int i=J.head[u];i;i=J.e[i].next){ int v=J.e[i].v; J.e[i].w+=J.h[u]-J.h[v]; } for(R int i=1;i<=n;++i){ J.dijkstra(i); long long ans=0; for(R int j=1;j<=n;++j){ if(J.dis[j]==J.dis[n+1])ans+=j*inf; else ans+=j*(J.dis[j]+J.h[j]-J.h[i]); } writeln(ans); } }