引言
全源最短路,说白了就是从任意一个点出发到任意一个点的最短路,也就是所有点对之间的最短路。以下是几种可能用得到的算法:
- floyd,三重循环,时间复杂度O(n3n^3n3)。
- dijkstra,循环起点进行单源最短路,单次时间复杂度O(mlogmmlogmmlogm),总时间复杂度O(nmlogmnmlogmnmlogm)。
显然,第2种写法时间复杂度更优,但他跑不了负边权呀,咋办?等着题目超时?就在这时,我们的johnson马上就要出场了,但我们还是得先把floyd讲完,不然蒟蒻编不下去了呀!
Flody
flody,编写起来容易,道理也是通俗易懂、简单粗暴。怎么说呢,你可以把它想成一个动态规划,dis[i][j]dis[i][j]dis[i][j]就代表iii点到jjj点的最短路长度,第一层循环就枚举间接节点,第二三层枚举iii和jjj,那么动态转移方程就是dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j])dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j])dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j])。
int dis[105][105];
for(int k=1;k<=n;k++){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(k!=i&&i!=j&&k!=j)dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
}
}
}
Johnson
johnson,人如其名,听起来就是很厉害的样子。其实他就是dijkstra,啥?你没听错,不过是dijkstra的优化版,还参杂了一点bellman,由于dijkstra的短板是不能跑负边权图,所以我们就要把负边权图变成非负边权图。
-
我们先建立一个超级源点,把他跟每一个节点各连一条边权为0的边。//////让他指向其他节点
-
从源点出发,运行bellman,计算原点到每个结点的最短路径,用h数组统计。
-
对于每条边(u,v),把边权调整为w+h[u]-h[v],可以证明经过调整边权一定非负。
-
从每个节点出发,跑一边dijkstra,求出全源最短路,最后输出答案。

证明
我们可以把h想成一种势能,因为势能只跟起点和终点有关,所以dijkstra求出答案后只要减去h[u]加上h[v],就能得出最终结果。
如上图,假设我们要求1到3的最短路,先从1走到2,此时边权和为3+h[1]-h[2],接着再从2走到3,此时总边权为3+h[1]-h[2]-2+h[2]-h[3],结合起来就是h[1]-h[3]+1。
此时注意,这个结果仅仅只是dijkstra求出的结果,而我们想要是那个1,所以最终答案为dijkstra求出的结果再减去起点的势能再加上终点的势能。
接下来证明新图上的边权值均非负:
h是从超级源点出发到每个点的最短距离,所以对于任意一条边(u, v, w),一定有hv <= hu + w,上式移项得w + hu - hv >= 0,即新图中的边权非负。
实现
const ll Maxn=1e4,inf=1e9;
ll n,m,head[Maxn],tot,dis[Maxn],h[Maxn];
bool vis[Maxn];
ll cnt[Maxn];
struct edge1{
ll u,v,w,Next;
}Edge[Maxn<<1];
inline void add(ll u,ll v,ll w){
Edge[++tot]=(edge1){u,v,w,head[u]},head[u]=tot;
}
inline bool SPFA(ll s){
for(ll i=1;i<=n;i++) h[i]=inf;
h[s]=0;
vis[s]=1;
queue<ll>que;
que.push(s);
while(!que.empty()){
ll u=que.front();
que.pop();
vis[u]=0;
for(ll i=head[u];i;i=Edge[i].Next){
ll v=Edge[i].v;
if(h[u]+Edge[i].w<h[v]){
h[v]=h[u]+Edge[i].w;
if(!vis[v]){
if(++cnt[v]>n+1)
return 0;
vis[v]=1;
que.push(v);
}
}
}
}
return 1;
}
struct node{
ll v,num;
bool operator <(const node &x)const{
return num>x.num;
}
};
inline void Dijkstra(ll s){
for(ll i=0;i<=n;i++) dis[i]=inf,vis[i]=0;
dis[s]=0;
priority_queue<node>que;
que.push((node){s,0});
while(!que.empty()){
ll u=que.top().v;
que.pop();
if(vis[u]) continue;
vis[u]=1;
for(ll i=head[u];i;i=Edge[i].Next){
ll v=Edge[i].v;
if(dis[u]+Edge[i].w<dis[v]){
dis[v]=dis[u]+Edge[i].w;
if(!vis[v]) que.push((node){v,dis[v]});
}
}
}
} //该代码出自 知乎 作者秋钧《单源(全源)最短路及其应用》
完结撒花***\ (^ – ^) /***
1310






