Loading

johnson全源最短路

简介

一下都假设该有向图(无向图同理)有n个点,m条边。

谈及全源最短路,第一个想到的是弗洛伊德算法,简单有效,因为并非本篇文章重点,所以只是把代码放在这里:

int main(){
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			for(int q=1;q<=n;q++)
				if(d[j][p]>d[j][i]+d[i][q])
					d[j][p]=d[j][i]+d[i][q];
}

唯一要注意的就是必须要枚举转折点。时间复杂度O(n^3)$,当n很大时不是一个可以接受的数字。

或者跑n遍单元最短路。你可以用spfa跑,这样的时间复杂度为\(O(n^2m)\)可以处理负边权。

顺便一提,spfa时间复杂度稀疏图约为\(O(m*2)\),稠密图\(O(m*INF)\)很大一个数

有dijkstra跑的话是\(O(n^2logn)\),加了堆优化,无法处理负边权。

可以发现,以上三种情况都不是全能的,且时间复杂度不够优。所以我们需要一个更有效的方法。

Johnson全源最短路

因为dijkstra是目前时间复杂度最优的,所以用dijkstra跑是最理想的,那么我们需要解决负边权的问题。

第一步:添加一个结点(结点0)向所有结点连一个边权为0的边,从这个点跑一遍spfa,检验是否存在负环,同时得到结点0到所有点的最短路,结点u对于结点0的最短路记为\(h_u\),同时,把起点为u终点为v的边的权值更新为\(w_{u,v}+h_u-h_v\)。因为h数组存的是关于结点0的最短路,多有一定有\(h_v\le h_u+w_{u,v}\) 否则,不等号右边一定可以更新不等号左边,与h数组的定义不符。所以,在做完后所有边的权值变成了非负值,我们可以跑dijkstra。

那么这么做的正确性在哪里?

s到t的路径中随便取出一条 \(𝑠−>𝑝1−>𝑝2−>⋯−>𝑝𝑘−>𝑡\)

则这条路径长度为 \((𝑤_{𝑠,𝑝1}+ℎ_𝑠−ℎ_{𝑝1})+(𝑤_{𝑝1,𝑝2}+ℎ_{𝑝1}−ℎ_{𝑝2})+⋯+(𝑤_{𝑝𝑘,t}+ℎ_{𝑝𝑘}−ℎ_𝑡)\)

化简得到 \(w_{s,p1}+w_{p1,p2}+...+w{pk,t}+h_s-h_t\)

对于从s到t的所有路径,\(h_s,h_t\)是固定的,所以从s到t的最短路在进行了这个操作后最短路路径不变。

第二步:跑dijkstra

第三步:输出答案

记得一定要\(-h_s+h_t\)

代码

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<sstream>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<deque>
#include<cstdlib>
#include<ctime>
#define dd double
#define ll long long
#define ull unsigned long long
#define N 10000
#define M 3010
using namespace std;

const ll INF=1e9;
const ll IINF=0x3f3f3f3f;
int n,m;

struct edge{
	int from,to,w,next;
	inline void intt(int from_,int to_,int w_,int next_){
		from=from_;to=to_;w=w_;next=next_;
	}
};
edge li[N];
int head[M],tail;

inline void add(int from,int to,int w){
	li[++tail].intt(from,to,w,head[from]);
	head[from]=tail;
}

ll h[M];
struct SPFA{
	queue<int> q;bool vis[M];
	int how_v[M];
	
	inline bool spfa(int u){
		while(!q.empty()) q.pop();
		memset(h,IINF,sizeof(h));
		memset(how_v,0,sizeof(how_v));
		memset(vis,0,sizeof(vis));
		h[u]=0;q.push(u);vis[u]=1;
		while(!q.empty()){
			int top=q.front();q.pop();
//			printf("%d\n",top);
			vis[top]=0;
			how_v[top]++;if(how_v[top]==n+1) return 0;
//			int k=head[top];printf("%d \n",top);
			for(int k=head[top];k;k=li[k].next){
				int to=li[k].to;
//				printf("%d\n",h[to]);
				if(h[to]>h[top]+li[k].w){
					h[to]=h[top]+li[k].w;
					if(!vis[to]){
						q.push(to);vis[to]=1;
					}
				}
			}
		}
		return 1;
	}
};
SPFA s;

struct DIJ{
	struct rode{
		int sum;
		ll d;
		rode() {}
		rode(int sum,ll d) : sum(sum),d(d) {}
	};
	
	struct cmp{
		inline bool operator () (rode a,rode b){
			return a.d>b.d;
		}
	};
	
	priority_queue<rode,vector<rode>,cmp> q;
	bool vis[M];ll d[N];
	
	inline void dij(int u){
		memset(d,IINF,sizeof(d));
		while(!q.empty()) q.pop();
		memset(vis,0,sizeof(vis));
		d[u]=0;q.push(rode(u,d[u]));
		while(!q.empty()){
			rode fr=q.top();q.pop();
			if(vis[fr.sum]) continue;
			vis[fr.sum]=1;
			for(int k=head[fr.sum];k;k=li[k].next){
				int to=li[k].to;
				if(d[to]>d[fr.sum]+li[k].w){
					d[to]=d[fr.sum]+li[k].w;
					q.push(rode(to,d[to]));
				}
			}
		}
	}
	
};
DIJ di;

int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
		int from,to,w;
		scanf("%d%d%d",&from,&to,&w);
		add(from,to,w);
	}
	for(int i=1;i<=n;i++) add(n+1,i,0);
//	for(int k=head[n+1];k;k=li[k].next) printf("%d ",li[k].to);
	
	if(!s.spfa(n+1)){
		printf("-1");
		return 0;
	}
//	for(int i=1;i<=n+1;i++) printf("%d ",s.how_v[i]);
//	while(1);
	for(int i=1;i<=m;i++) li[i].w+=h[li[i].from]-h[li[i].to];
	for(int i=1;i<=n;i++){
		di.dij(i);
		ull ans=0;
		for(int j=1;j<=n;j++){
			if(di.d[j]>INF) ans+=INF*j;
			else ans+=(j*(di.d[j]+h[j]-h[i]));
//			printf("%lld ",di.d[j]);
		}
//		while(1);
		printf("%lld\n",ans);
	}
	return 0;
}

引用

https://www.cnblogs.com/mk-oi/p/13604088.html

posted @ 2021-02-16 22:48  hyl天梦  阅读(82)  评论(0编辑  收藏  举报