【图论】Johnson全源最短路 笔记
简介
-
用于稀疏图求解全源最短路。
-
时间复杂度(优先队列)
O(VE\log E) ,使用斐波那契堆可优化至O(VE+V^2\log V) ,稀疏图上渐进复杂度优于\text{Floyd-Warshall} 算法。 -
返回一个包含所有结点对的最短路径权重的矩阵或报告存在负环。
原理
- 重新赋予权重
\text{reweight}
若图
新赋予的权重
- 对于所有结点对
u,\space v\in V ,一条路径p 是在使用权重函数w 时从结点u 到结点v 的一条最短路径,当且仅当p 是在使用权重函数w' 时从u 到v 的最短路径。 - 对于所有的边
(u,\space v) ,新权重w'(u,\space v) 非负。
- 具体实现
新建一个虚拟结点
设
由裂项相消和可得,
由于
对于环路
故满足性质
而根据三角形不等式,定义
故满足性质
代码实现
题目来源:【模板】Johnson 全源最短路
伪代码:略。
C++:
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
const int __=6e3+10;
const int _=3e3+10;
const int inf=2147483647;
using namespace std;
int n,m;
struct Edge{
int to[__<<1],dis[__<<1],next[__<<1],head[_],tot;
void add(int u,int v,int w){
to[++tot]=v,dis[tot]=w,next[tot]=head[u],head[u]=tot;
}
}e;
struct node{
int u,dis;
node(){}
node(int x,int w){u=x,dis=w;}
bool operator <(const node &a)const{
return dis>a.dis;
}
};
int h[_];bool vis[_];
int ct[_];
queue<int> q1;
priority_queue<node> q;
bool spfa(){
for(int i=1;i<=n;i++) h[i]=inf;
q1.push(0);vis[0]=1;
while(q1.size()){
int u=q1.front();q1.pop();
vis[u]=0;
for(int i=e.head[u],y;i;i=e.next[i]){
if((long long)h[y=e.to[i]]>(long long)h[u]+e.dis[i]){
h[y]=h[u]+e.dis[i];
ct[y]=ct[u]+1;
if(ct[y]>n) return 1;
if(!vis[y]){
vis[y]=1;
q1.push(y);
}
}
}
}
return 0;
}
int dis[_];
void Dijkstra(int s){
for(int i=1;i<=n;i++) dis[i]=inf;
memset(vis,0,sizeof vis);
dis[s]=0;
q.push(node(s,0));
while(q.size()){
int u=q.top().u;q.pop();
if(vis[u]) continue;
vis[u]=1;
for(int i=e.head[u],v;i;i=e.next[i]){
if(dis[v=e.to[i]]>dis[u]+e.dis[i]){
dis[v]=dis[u]+e.dis[i];
if(!vis[v]) q.push(node(v,dis[v]));
}
}
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1,u,v,w;i<=m;i++){
scanf("%d%d%d",&u,&v,&w);
e.add(u,v,w);
}
for(int i=1;i<=n;i++) e.add(0,i,0);
if(spfa()){
puts("-1");return 0;
}
for(int i=1;i<=n;i++){
for(int j=e.head[i];j;j=e.next[j]){
e.dis[j]+=h[i]-h[e.to[j]];
}
}
for(int i=1;i<=n;i++){
Dijkstra(i);
long long ans=0;
for(int j=1;j<=n;j++){
if(dis[j]==inf) ans+=(long long)j*(int)1e9;
else ans+=(long long)j*(dis[j]+h[j]-h[i]);
}
printf("%lld\n",ans);
}
return 0;
}
参考资料
-
《算法导论》
-
[洛谷日报#242]Johnson 全源最短路径算法学习笔记 -StudyingFather的博客