Johnson全源最短路:负权化正权,最后减去势能差

Johnson全源最短路:负权化正权,最后减去势能差

(1)建虚点0,add(0,i,0),跑st=0的单源最短路hi

(2)e[i].w+=h[u]-h[v]

​ Q:为何这样不会得到错误答案?

​ A:[ 最短路 - OI Wiki ]()

(3)O(N^2*logN)跑n次dijk

Code:(关键是要能熟练写出dijk和spfa)

#include<bits/stdc++.h>
#define F(i,l,r) for(int i=l;i<=r;++i)
#define G(i,r,l) for(int i=r;i>=l;--i)
#define ll long long
#define mem(a) memset(a,0,sizeof(a))
using namespace std;
priority_queue<pair<int,int> >pq;
const int N=3e3+5;
const int inf=1e9;
struct node{
	int v,ne,w;
}e[N<<2];
int n,m,k=0,cnt[N],first[N];
bool vis[N];
ll sum=0,h[N],dis[N][N];
inline void add(int x,int y,int z){ e[++k].v=y; e[k].w=z; e[k].ne=first[x]; first[x]=k; }
inline bool spfa(){
	mem(vis); queue<int> q; q.push(0); vis[0]=1; h[0]=0; cnt[0]=1;
	F(i,1,n) h[i]=inf;
	while(q.size()){
		int u=q.front(); q.pop(); vis[u]=0;//出队
		for(int i=first[u];i;i=e[i].ne){
			int v=e[i].v,w=e[i].w;
			if(h[v]>h[u]+w){
				h[v]=h[u]+w;
				cnt[v]=cnt[u]+1;
				if(cnt[v]>=n+2) return 1;
				if(!vis[v]) q.push(v),vis[v]=1;
			}
		} 
	}
	return 0;
}
inline void dijk(int st){
	mem(vis); pq.push(make_pair(0,st));
	while(!pq.empty()){
		int u=pq.top().second; pq.pop();
		if(vis[u]) continue; vis[u]=1;//u已访问过 
		for(int i=first[u];i;i=e[i].ne){
			int v=e[i].v,w=e[i].w;
			if(dis[st][v]>dis[st][u]+w){
				dis[st][v]=dis[st][u]+w;
				if(!vis[v]) pq.push(make_pair(-dis[st][v],v));
			}
		}
	}
}
int main(){
	scanf("%d%d",&n,&m); F(i,1,n) add(0,i,0);
	int u,v,w; F(i,1,m){
		scanf("%d%d%d",&u,&v,&w);
		add(u,v,w);
	}
	if(spfa()) { puts("-1"); return 0;} 
	F(i,1,n) F(j,1,n) i!=j?dis[i][j]=inf:dis[i][j]=0;
	F(u,1,n) for(int i=first[u];i;i=e[i].ne) e[i].w+=h[u]-h[e[i].v];
	F(i,1,n){
		dijk(i); sum=0;
		F(j,1,n) sum+=dis[i][j]==inf ? 1ll*j*inf : 1ll*j*(dis[i][j]-h[i]+h[j]);//对于dis[i][j]=inf,不需要处理势能差 
		printf("%lld\n",sum);
	}
	return 0;
}
posted @ 2023-10-07 20:34  superl61  阅读(10)  评论(0)    收藏  举报