1. 程式人生 > 實用技巧 >【模板】Johnson 全源最短路

【模板】Johnson 全源最短路

https://www.zybuluo.com/ysner/note/1744553

題面

戳我

思考

首先來區分一下\(Dijkstra\)\(SPFA\)
\(Dij\)開局不標\(vis=1\)
\(Dij\)用某點更新的條件是\(vis=0\)
\(SPFA\)中的\(vis\)表示點是否在佇列中。

//SPFA
    queue<int>Q;
	memset(d,63,sizeof(d));d[0]=0;vis[0]=1;Q.push(0);
	while(!Q.empty())
	{
		int u=Q.front();Q.pop();
		for(int i=h[u];i;i=e[i].nxt)
		{
		  int v=e[i].to;
		  if(d[v]>d[u]+e[i].w)
		  {
			a[v]=a[u]+1;
			if(a[v]>n) return 1;//a代表最短路徑所含點數,顯然不能超過n
			d[v]=d[u]+e[i].w;
			if(!vis[v]) vis[v]=1,Q.push(v);
		  }
	    }
		vis[u]=0;//
	}
//Dijkstra
priority_queue<node>Q;
    for(int i=1;i<=n;i++) dis[i]=1e9,vis[i]=0;
    Q.push((node){s,0});dis[s]=0;
    while(!Q.empty())
    {
      int u=Q.top().u;Q.pop();
      if(vis[u]) continue;vis[u]=1;//
      for(int i=h[u];i;i=e[i].nxt)
      {
      	int v=e[i].to;
      	if(dis[v]>dis[u]+e[i].w)
      	{
      		dis[v]=dis[u]+e[i].w;
      		if(!vis[v]) Q.push((node){v,dis[v]});
      	}
      }
    }

然後\(Dij\)不能用於負權圖,故只能把圖修成正權的。
怎麼辦?利用\(dis[v]<=dis[u]+e[i].w\),我們可以把邊權改成\(e[i].w+dis[u]-dis[v]\),而這個一定正。
走任意一條路後,可以發現真實距離就是\(\sum dis+dis[v]-dis[u]\)\(v\)為終點,\(u\)為起點)。因為中途點的\(dis\)一正一負抵消掉了。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=3e3+3;
struct Edge{int to,nxt,w;}e[N<<2];
struct node{int u,dis;bool operator < (const node &o) const{return dis>o.dis;}};
int n,m,h[N],a[N],cnt;
ll dis[N],d[N],sum;
bool vis[N];
int gi()
{
	int x=0,t=1;
	char ch=getchar();
	while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
	if(ch=='-') t=-1,ch=getchar();
	while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
	return x*t;
}
void add(int u,int v,int w){e[++cnt]=(Edge){v,h[u],w};h[u]=cnt;}
bool SPFA()
{
	queue<int>Q;
	memset(d,63,sizeof(d));d[0]=0;vis[0]=1;Q.push(0);
	while(!Q.empty())
	{
		int u=Q.front();Q.pop();
		for(int i=h[u];i;i=e[i].nxt)
		{
		  int v=e[i].to;
		  if(d[v]>d[u]+e[i].w)
		  {
			a[v]=a[u]+1;
			if(a[v]>n) return 1;
			d[v]=d[u]+e[i].w;
			if(!vis[v]) vis[v]=1,Q.push(v);
		  }
	    }
		vis[u]=0;
	}
	return 0;
}
void Dijstra(int s)
{
    priority_queue<node>Q;
    for(int i=1;i<=n;i++) dis[i]=1e9,vis[i]=0;
    Q.push((node){s,0});dis[s]=0;
    while(!Q.empty())
    {
      int u=Q.top().u;Q.pop();
      if(vis[u]) continue;vis[u]=1;
      for(int i=h[u];i;i=e[i].nxt)
      {
      	int v=e[i].to;
      	if(dis[v]>dis[u]+e[i].w)
      	{
      		dis[v]=dis[u]+e[i].w;
      		if(!vis[v]) Q.push((node){v,dis[v]});
      	}
      }
    }
    for(int i=1;i<=n;i++) 
      if(dis[i]==1e9) sum+=i*dis[i];
      else sum+=i*(dis[i]+d[i]-d[s]);
    printf("%lld\n",sum);sum=0;
}
int main()
{
	n=gi();m=gi();
	while(m--)
	{
		int u=gi(),v=gi(),w=gi();
		add(u,v,w);
	}
	for(int i=1;i<=n;i++) add(0,i,0);
	if(SPFA()) {puts("-1");return 0;}
	for(int u=1;u<=n;u++)
	  for(int i=h[u];i;i=e[i].nxt)
	    e[i].w+=(d[u]-d[e[i].to]);
	for(int i=1;i<=n;i++)
	  Dijstra(i);
	return 0;
}