一下都假设该有向图(无向图同理)有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
由于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; }