johnson全源最短路

簡介

一下都假設該有向圖(無向圖同理)有n個點,m條邊。html

談及全源最短路,第一個想到的是弗洛伊德算法,簡單有效,由於並不是本篇文章重點,因此只是把代碼放在這裏:ios

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很大時不是一個能夠接受的數字。c++

或者跑n遍單元最短路。你能夠用spfa跑,這樣的時間複雜度爲\(O(n^2m)\)能夠處理負邊權。算法

順便一提,spfa時間複雜度稀疏圖約爲\(O(m*2)\),稠密圖\(O(m*INF)\)很大一個數數組

有dijkstra跑的話是\(O(n^2logn)\),加了堆優化,沒法處理負邊權。優化

能夠發現,以上三種狀況都不是全能的,且時間複雜度不夠優。因此咱們須要一個更有效的方法。spa

Johnson全源最短路

由於dijkstra是目前時間複雜度最優的,因此用dijkstra跑是最理想的,那麼咱們須要解決負邊權的問題。code

第一步:添加一個結點(結點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。htm

那麼這麼作的正確性在哪裏?blog

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;
}

引用

http://www.javashuo.com/article/p-dlpkifss-vc.html

相關文章
相關標籤/搜索