【模板】Johnson
既然\(Floyd\)是\(\operatorname{O}(V^3)\),而\(Dijkstra\)是\(\operatorname{O}(V\log(E))\),那我们就跑\(V\)遍\(Dijkstra\)吧!
如果有负环的话就用\(SPFA\)先判一下,为了防止负边权给\(Dijkstra\)造成麻烦,我们就用一个函数\(h(i)\)将一个点对应到整数上。
令:$$\overline\omega(u,v)=\omega(u,v)+h(u)-h(v)$$
跑\(Dijkstra\)得\(dis(u,v)(u,v\in V)\)
则此时最后的$$dis(u,v)=\overline{dis}(u,v)-h(u)+h(v)$$
#include <stdio.h>
#include <queue>
#include <cstring>
using namespace std;
const int z = 1024;
int n, m;
struct EDGE {
int f, t, next;
int w;
} edge[z<<2];
int head[z], tot;
int h[z];
void add_edge(int f,int t,int w) {
edge[++tot].next = head[f];
edge[tot].f = f;
edge[tot].t = t;
edge[tot].w = w;
head[f] = tot;
}
#define nw(x) (edge[x].w+h[edge[x].f]-h[edge[x].t])
bool vis[z];
int index[z];
bool SPFA(int start) {
queue<int> q;
memset(vis,false,sizeof(vis));
memset(h,0x7f,sizeof(h));
memset(index,0,sizeof(index));
q.push(start);
vis[start] = true;
h[start] = 0;
while(!q.empty()) {
int u = q.front();
q.pop();
vis[u] = false;
for(int i = head[u];i;i = edge[i].next) {
int v = edge[i].t;
if(h[v] > h[u]+edge[i].w) {
h[v] = h[u]+edge[i].w;
if(!vis[v]) {
q.push(v);
vis[v] = true;
if(++index[v] > n)
return false;
}
}
}
}
return true;
}
int dis[z][z];
struct NODE {
int id, data;
bool operator < (const NODE x) const {
return x.data < data;
}
} hp;
void Dijkstra(int start) {
priority_queue<NODE> q;
q.push(NODE{start,0});
memset(dis[start],0x7f,(n+1)*sizeof(int));
memset(vis,false,(n+1)*sizeof(bool));
dis[start][start] = 0;
while(!q.empty()) {
int u = q.top().id;
int d = q.top().data;
q.pop();
if(!vis[u]) {
vis[u] = true;
for(int i = head[u];i;i = edge[i].next) {
int v = edge[i].t;
if(dis[start][v] > d+nw(i)) {
dis[start][v] = d+nw(i);
q.push(NODE{v,dis[start][v]});
}
}
}
}
}
bool Johnson() {
if(SPFA(1)) {
for(int i = 1;i <= n;++i)
add_edge(n+1,i,0);
SPFA(n+1);
/*for(int i = 1;i <= n;++i)
printf("%d ",h[i]);
putchar(10);
for(int i = 1;i <= m;++i)
printf("%d -> %d :: %d \n",edge[i].f,edge[i].t,nw(i));
putchar(10);*/
for(int i = 1;i <= n;++i) {
Dijkstra(i);
/*for(int j = 1;j <= n;++j)
printf("%10d ",dis[i][j]);
putchar(10);*/
for(int j = 1;j <= n;++j)
dis[i][j] = dis[i][j]-h[i]+h[j];
}
} else {
return false;
}
}
signed main() {
scanf("%d %d",&n,&m);
for(int i = 1, a, b, c;i <= m;++i) {
scanf("%d %d %d",&a,&b,&c);
add_edge(a,b,c);
}
Johnson();
for(int i = 1;i <= n;++i) {
printf("%2d :: ",i);
for(int j = 1;j <= n;++j)
printf("%10d ",dis[i][j]);
putchar(10);
}
}