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;
}

浙公网安备 33010602011771号