最短路总结
1.最短路
u->v之间的最短路满足以下限制: 对任意k ∈ G(V,E) 有 dist u,v <= dis u,k + dis k,j
关键操作-松弛
void relax(int i,int j,int k){
if(dis[k]>dis[i]+j)
dis[k]=dis[i]+j;
}
2.多源点多汇点最短路
Floyd
void Floyd(){
for(int k=1;k<=n;++k)
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
if(dis[i][j]>dis[i][k]+dis[k][j])
dis[i][j]=dis[i][k]+dis[k][j];
}
复杂度 O(V^3) 可处理负环
拓展
把所有边存成负的可以作为最长路。
3.单源点多汇点最短路
Bellman-Ford
void Bellman_ford(int s){
memset(dis,inf,sizeof(dis));
dis[s]=0;
for(int i=1;i<n;++i)
for(int j=1;j<=m;++j)
relax(Edge[j].u,Edge[j].w,Edge[j].v);
}
复杂度O(V*E) 可处理负环(判断是否还能松弛)
SPFA
queue<int> q;
void SPFA(int s){
memset(dis,inf,sizeof(dis));
q.push(s);
vis[s]=1;
dis[s]=0;
while(!q.empty()){
const int u=q.front();
q.pop();
vis[u]=0;
for(int e=pre[u];e;e=nx[e]){
const int v=to[e];
if(dis[v]>dis[u]+w[e]){
dis[v]=dis[u]+w[e];
if(!vis[v]) q.push(v),vis[v]=1;
}
}
}
}
复杂度O(k*E) 可处理负环(一个点入队次数超过n次) 加上SLF和LLL优化后k很小
Dijkstra
void Dijkstra(int s){
memset(dis,inf,sizeof(dis));
dis[s]=0;
for(int i=1;i<=n;++i){
int maxs=inf,u=0;
for(int j=1;j<=n;++j)
if(!vis[j]&&dis[j]<maxs)
u=j,maxs=dis[j];
vis[u]=1;
for(int e=pre[u];e;e=nx[e]){
const int v=to[e];
if(!vis[v]&&dis[v]>dis[u]+w[e]) dis[v]=dis[u]+w[e];
}
}
}
复杂度O(V^2+E) 不可处理负环 可用heap,线段树等优化到O((V+E)logV),O(VlogV+E)
4.多源点单汇点最短路
存逆图跑单源最短路即可。
5.DAG最短/最长链
//对于一个DAG可以得知它的拓扑序,从而通过DP解决问题
queue<int> q;
int DAG_shortest_path(){
memset(dis,inf,sizeof(dis));
for(int i=1;i<=n;++i)
if(degree[i]==0) q.push(i);
while(!q.empty()){
const int u=q.front();
q.pop();
topo[++topo[0]]=u;
for(int e=pre[u];e;e=nx[e]){
const int v=to[e];
--degree[v];
if(degree[v]==0) q.push(v);
}
}
ans=inf;
for(int i=1;i<=topo[0];++i){
const int u=topo[i];
for(int e=pre[u];e;e=nx[e]){
const int v=to[e];
dis[v]=min(dis[v],dis[u]+w[e]);
}
ans=min(ans,dis[u]);
}
return ans;
}
复杂度O(V+E)
6.最短路算法的选择
对于稀疏图
首选自然是SPFA,好打而且跑得快,而且几乎没有考试丧心病狂到卡SPFA。 如果实在不放心,就选择priority_queue+Dijkstra吧。
对于稠密图
如果点数很小而且要求任意两个点之间的距离,那么就Floyd吧 否则首选是Dijkstra+priority_queue,至于为什么不选择朴素Dijkstra,因为它比较长。
对于有负权的图
SPFA
对于DAG且不要求源点与汇点
DAG最短/最长链
总之
SPFA和priority_queue Dijkstra比较实用,一般会这两个就行了。 最好写的是Floyd。 DAG最短/最长链在某些题目中很重要。 Bellman-Ford没卵用。