【模板】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;
}