样例题的话随便找个Floyd算法自己测一下吧,下面上封装后的Johnson板子:
(好啦…既然你们懒那我给你们找个吧。。AcWing.854)
链接:https://www.acwing.com/problem/content/description/856/
#include<bits/stdc++.h>
#define ll long long
#define white 0
#define black 1
#define grey 2
#define endl '\n'
#define IO ios::sync_with_stdio(false);cin.tie(0);
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn=1e3+5;
ll n,m,k;ll d[maxn],backup[maxn],h[maxn];
ll dis[maxn][maxn],dd[maxn],color[maxn],target[maxn][maxn];
struct node{
ll u,v,w;
}edge[25000];
void Bellman_ford(ll s){
memset(d,INF,sizeof(d));
d[s]=0;
for(ll i=1;i<=n+1;i++){
memcpy(backup,d,sizeof(d));
for(ll j=1;j<=m+n;j++){
ll u=edge[j].u,v=edge[j].v,w=edge[j].w;
if(backup[u]!=INF&&d[v]>backup[u]+w){
d[v]=backup[u]+w;
}
}
}
}
void dijkstra(ll s){
for(ll i=1;i<=n;i++){
dd[i]=INF,color[i]=white;
}
dd[s]=0;color[s]=grey;
while(1){
ll mind=INF,u=-1;
for(ll i=1;i<=n;i++){
if(dd[i]<mind&&color[i]!=black){
mind=dd[i];
u=i;
}
}
color[u]=black;
if(u==-1) break;
for(ll v=1;v<=n;v++){
if(dis[u][v]!=INF&&color[v]!=black){
if(dd[v]>dd[u]+dis[u][v]){
dd[v]=dd[u]+dis[u][v];
color[v]=grey;
}
}
}
}
}
int main(){
cin>>n>>m>>k;
for(ll i=1;i<=m;i++){
ll u,v,w;
cin>>edge[i].u>>edge[i].v>>edge[i].w;
}
for(ll i=m+1;i<=m+n;i++){
edge[i].u=0;edge[i].v=i-m;edge[i].u=0;
}
Bellman_ford(0);
for(ll i=1;i<=n;i++) {h[i]=d[i];}
for(ll i=1;i<=n;i++){
for(ll j=1;j<=n;j++){
dis[i][j]=INF;
if(i==j) dis[i][j]=0;
}
}
for(ll i=1;i<=m;i++){
ll u=edge[i].u,v=edge[i].v,w=edge[i].w;
if(dis[u][v]>w+h[u]-h[v]) dis[u][v]=w+h[u]-h[v];
}
for(ll i=1;i<=n;i++){
dijkstra(i);
for(ll j=1;j<=n;j++){
target[i][j]=dd[j]+h[j]-h[i];
}
}
while(k--){
ll s,e;cin>>s>>e;
if(target[s][e]<INF/2) cout<<target[s][e]<<endl;
else puts("impossible");
}
}
/*
3 3 2
1 2 1
2 3 2
1 3 1
2 1
1 3
output:
impossible
1
*/
相比较Floyd算法O(n^3)的复杂度,Johnson算法将时间复杂度优化到了O(nnlogn),快了一个量级。
但是呢,代码量是远大于Floyd算法的,而且我搜了很多官方的材料(搭梯子就不多说了没啥用),讲的都很片面,而且呢那些代码也是出了奇的丑陋…根本不适合萌新去阅读和理解。所以我自己亲自写一份吧。
步入正题:
Johnson 和 Floyd 一样,是一种能求出无负环图上任意两点间最短路径的算法。该算法在 1977 年由 Donald B. Johnson 提出。
emmm,大概意思我解释一下:就是你先新建立一个节点node(4),然后通过Bellman_Ford算法求其到任何一个点的最短路径h[i],为什么要用Ford算法呢,因为Dijskstra算法处理不了负边(原理是Dijkstra算法是基于贪心的思想求最大边),而Ford可以处理负权边。
这样呢,我就把所有的边转化成正数了。
然后就利用Dijkstra算法去对每一个node点执行最短路操作,求出该点u到v的距离,并记录,最后线性查询就行了。记得更新最短距离的时候,是h[v]-h[u]。