全源最短路

编程学习

Posted by simplex on March 28, 2021

JOHNSON

https://zhuanlan.zhihu.com/p/99802850

主要思想就是先用一个虚节点连接到其他所有节点,边权为0,通过SPFA来计算虚节点到其他节点的最短的距离sp[i],其目的就是通过变换dst[e(u,v)] = dst[i] + sp[u] - sp[v]来获得全部都为正数的边人,然后逐个点进行dij

适用于稀疏图,稠密图直接bellmanford或者SPFA

// author: M@
#define m_
//#define _DEBUG_
#include<iostream>
#include<cstdio>
#include<string.h>
#include<iomanip>
#include<queue>
#include<vector>
#include<map>
#include<algorithm>
#include<cmath>
#include<list>
#include<set>
#include<string>
#define clr(a)   memset((a),0,sizeof (a))


using namespace std;
typedef unsigned int ui;
typedef long long LL;
typedef pair<int,int> pii;
//const int INF=0x3f3f3f3f;
const int INF = 1e9;
const LL LINF=1e18;
const double DINF=1e9;
const double EPS=1e-9;
const int maxn=3e3 + 5;
const int maxm = 9e3 + 5;
bool vis[maxn];
int n, m, Q;
struct edg{
	int to, nxt, w;
//	edg(int a, int b, int c):to(a), nxt(b), w(c){}
}e[maxm*2];
int cnt;
int head[maxn];
void add_edg(int fx, int x, int cost){
	e[++cnt] = {x, head[fx], cost}; 
	head[fx] = cnt;
}
LL sp[maxn];
LL dst[maxn];
queue<int> sq;
int scnt[maxn];
int spfa(){
	sq.push(0);
	while(sq.empty() == false){
		int idx = sq.front();
		sq.pop();
		vis[idx] = false;
		for(int i = head[idx]; i != 0; i = e[i].nxt){
			int to = e[i].to;
			LL d = sp[idx] + e[i].w;
			if(d < sp[to]){
				sp[to] = d;
				scnt[to] = scnt[idx] + 1;
				if(scnt[to] >  n){
					return -1;
				}
				if(vis[to]) continue;
				vis[to] = true;
				sq.push(to);
			}
		}
	}
	return 0;
}

struct node{
	int to;
	LL dst;
	bool operator<(const node & a)const{
		return dst >= a.dst;
	}
};
void dij(int idx){
	priority_queue<node> dq;
	dq.push({idx, 0});
	for(int i = 1; i <=n; i++){
		vis[i] = false;
		dst[i] = INF;
	}
	dst[idx] = 0;
	while(dq.empty() == false){
		node tn = dq.top();
		dq.pop();
		if(vis[tn.to])continue;
		vis[tn.to] = true;
		for(int i = head[tn.to]; i != 0; i = e[i].nxt){
			int to = e[i].to;
			LL d = tn.dst + e[i].w;
			if(d < dst[to]){
				dst[to] = d;
				dq.push({to, d});
			}
		}
	}
}

int main()
{
#ifdef m_
    freopen("input.txt","r",stdin);
//    freopen("output.txt","w",stdout);
#endif
	scanf("%d%d", &n, &m);
	for(int i = 0 ; i < m; i++){
		int u,v,w;
		scanf("%d%d%d", &u, &v, &w);
		add_edg(u, v, w);
	}
	for(int i = 1; i <= n; i++){
		add_edg(0, i, 0);
		sp[i] = 1e9;
	}
	sp[0] = 0;
	if(spfa() == -1){
		puts("-1");
	}
	else{
		
		for(int i = 1; i <= n; i++){
			for(int j = head[i] ; j != 0; j =e[j].nxt){
				e[j].w += sp[i] - sp[e[j].to];
			}
		}
		for(int i = 1; i <= n; i++){
//			cout<<"dij"<<endl;
			LL ans = 0;
			dij(i);
			for(int j = 1; j <= n; j++){
				
				if(dst[j] == INF){
					ans += j * dst[j];
				}
				else{
					ans += j * (dst[j] - sp[i] + sp[j]);
//					printf("%d ", dst[j] - sp[i] + sp[j]);
				}
			}
			printf("%lld\n", ans);
		}
	}


	
#ifdef m_
    fclose(stdin);
//    fclose(stdout);
#endif
    return 0;
}