Johnson 最短路算法

Johnson 算法

全源最短路径求解其实是单源最短路径的推广,求解单源最短路径的两种算法时间复杂度分别为:

  • Dijkstra 单源最短路径算法:时间复杂度为 \(O(E + VlogV)\),要求权值非负;
  • Bellman-Ford 单源最短路径算法:时间复杂度为 \(O(VE)\),适用于带负权值情况;

如果对全图顶点遍历,使用 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 算法了。

例题

【模板】Johnson 全源最短路

#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);
	}
}
posted @ 2022-03-19 08:36  PassName  阅读(77)  评论(0编辑  收藏  举报