Johnson 全源最短路

全源最短路,换一种说法就是n个单源最短路,可以用n次Bellman-Ford或SPFA,非负边权还可以用Dijkstra,可是有负边权用前两个算法还是慢,如果我们能把负边权映射成非负边权的话,一切就都好办了

这里我们引入一个虚拟结点,它和所有点的初始距离都是0,然后,我们求出来这个结点和其他店的最短路h,然后,我们将各个边边权变为w+h[u]-h[v](如果最短路不经过u,也就是不满足松弛操作h[v]>h[u]+w,如果经过u,h[v]=h[u]+w,所以一定非负)

然后,我们就可以用n次Heap-Dijkstra来求了

时间复杂度O(nm+n(mlogn))(SPFA+n*Heap-Dijkstra)
与floyed相比,此算法适合用在稀疏图

例题 洛谷 P5905 【模板】Johnson 全源最短路

#include<iostream>
#include<vector>
#include<queue>
#include<utility>
#include<cstring>
#define forup(i,l,r) for(int i=l;i<=r;i++)
using namespace std;
typedef long long ll;
const ll INF=1e9;
struct edge{ int v,w; };
vector<edge> node[3005];
int d[3005];
int n,m;

int h[3005];
queue<int> q;
bool ex[3005];
int cnt[3005];//判负环用的,见SPFA算法 
int SPFA(int s)
{
	memset(h,127,sizeof(h));
	h[s]=0; q.push(s); ex[0]=true;
	while(!q.empty())
	{
		int u=q.front();
		for(auto x:node[u])
		{
			int v=x.v,w=x.w;
			if(h[u]+w<h[v])
			{
				h[v]=h[u]+w;
				if(!ex[v]) {
					q.push(v); ex[v]=true;
					cnt[v]=cnt[u]+1;
					if(cnt[v]>n) return -1;
				}
			}
		}
		q.pop(); ex[u]=false;
	}
	return 0;
}
void dijkstra(int s)
{
	priority_queue<pair<int,int>> q;
	bool vis[3005]={};
	forup(i,1,n) d[i]=INF;
	d[s]=0;
	q.push({0,s});
	while(!q.empty())
	{
		int u=q.top().second; q.pop();
		if(vis[u]) continue;
		else vis[u]=true;
		for(auto x:node[u])
		{
			int v=x.v,w=x.w;
			if(d[u]+w<d[v]) d[v]=d[u]+w;
			q.push({-d[v],v});
		}
	}
}
int main()
{
	cin>>n>>m;
	int u,v,w;
	forup(i,1,m)
	{
		cin>>u>>v>>w;
		node[u].push_back({v,w});
	}
	forup(i,1,n) node[0].push_back({i,0});
	if(SPFA(0)==-1){//判负环的 
		cout<<-1;
		return 0;
	}
	forup(u,1,n)
	{
		for(auto &x:node[u])
		{
			x.w+=h[u]-h[x.v];//映射 
		}
	}
	forup(u,1,n)
	{
		dijkstra(u);
		ll ans=0;
		forup(v,1,n) 
			if(d[v]==INF) ans+=INF*v;
			else ans+=(ll)(d[v]-h[u]+h[v])*v;
		cout<<ans<<'\n';
	}
	return 0;
}

参考:https://www.cnblogs.com/dx123/p/16320444.html

posted @ 2023-05-24 20:09  eternal_visionary  阅读(15)  评论(0编辑  收藏  举报