【模板】Johnson 全源最短路
阿新 • • 發佈:2020-09-23
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; }