Johnson 全源最短路
作者:互联网
Johnson 全源最短路
Johnson 和 Floyd 一样,是一种能求出无负环图上任意两点间最短路径的算法。该算法在 1977 年由 Donald B. Johnson 提出。
Johnson 算法则通过另外一种方法来给每条边重新标注边权。
我们新建一个虚拟节点(在这里我们就设它的编号为0)。从这个点向其他所有点连一条边权为0的边。
接下来用SPFA算法求出从0号点到其他所有点的最短路,记为 dis
假如存在一条从点u到点v,边权为w的边,则我们将该边的边权重新设置为w += dis[u] - dis[v];
接下来以每个点为起点,跑 n轮 Dijkstra 算法即可求出任意两点间的最短路了。
最后加上dis[v] - dis[u];
容易看出,该算法的时间复杂度是O(nmlogm) 。
luogu P5905 Johnson 全源最短路
代码
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <queue>
#define INF 1000000000ll
using namespace std;
typedef long long ll;
const int N=2e4;
struct edge{
int from;
int to;
int dis;
int next;
}e[N];
int head[N],cnt;
int n,m,u,v,w;
void add(int u,int v,int w)
{
++cnt;
e[cnt].from = u;
e[cnt].to = v;
e[cnt].dis = w;
e[cnt].next = head[u];
head[u] = cnt;
}
int mapp[N];
int d[N];
int num[N];
bool vis[N];
queue<int> q;
bool SPFA(int u)
{
memset(mapp,0x3f,sizeof(mapp));
memset(vis,0,sizeof(vis));
mapp[u] = 0; vis[u] = true;
q.push(u);
while(!q.empty())
{
int x = q.front();
vis[x] = false;
q.pop();
for(int i=head[x];i;i=e[i].next)
{
int y = e[i].to;
if(mapp[y] > mapp[x] + e[i].dis)
{
mapp[y] = mapp[x] + e[i].dis;
if(vis[y] == false)
{
vis[y] = true;
num[y]++;
q.push(y);
}
if(num[y] >= n + 1) return true;
}
}
}
return false;
}
struct node{
int dis;
int pos;
bool operator <(const node &x)const
{
return x.dis < dis;
}
};
priority_queue<node> Q;
void Dijkstra(int u)
{
memset(d,0x3f,sizeof(d));
memset(vis,0,sizeof(vis));
d[u] = 0;
Q.push((node){0,u});
while(!Q.empty())
{
node p = Q.top();
Q.pop();
int x = p.pos;
if(vis[x]) continue;
vis[x] = 1;
for(int i=head[x];i;i=e[i].next)
{
int y = e[i].to;
if(d[y] > d[x] + e[i].dis)
{
d[y] = d[x] + e[i].dis;
Q.push((node){d[y],y});
}
}
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
}
for(int i=1;i<=n;i++)
add(n+1,i,0);
bool flag = SPFA(n+1);
if(flag == true)
{
puts("-1");
return 0;
}
for(int i=1;i<=n;i++)
{
for(int j=head[i];j;j=e[j].next)
e[j].dis += mapp[i] - mapp[e[j].to];
}
for(int i=1;i<=n;i++)
{
Dijkstra(i);
ll sum = 0;
for(int j=1;j<=n;j++)
{
if(d[j] == 1061109567)
{
sum += (ll)j*INF;
}
else sum += (ll)j*(d[j] + mapp[j] - mapp[i]);
}
printf("%lld\n",sum);
}
return 0;
}
标签:cnt,Johnson,int,全源,vis,mapp,短路,dis 来源: https://www.cnblogs.com/Elgina/p/15359297.html