最短路总结

背景:

只是想来备份一下最近的代码,顺便总结一下三种最短路算法的应用。

基础代码:

dijkstra:

 1 struct nd{int d,u; bool operator < (const nd &a)const{return d>a.d;}};
 2 int d[maxn]; bool vis[maxn]; priority_queue<nd>q;
 3 void dijkstra(int s){
 4     inc(i,1,n)d[i]=INF; d[s]=0; q.push((nd){0,s});
 5     while(!q.empty()){
 6         int x; while(!q.empty()&&vis[x=q.top().u])q.pop(); if(vis[x])break; vis[x]=1;
 7         for(int i=g[x];i;i=es[i].n)if(d[es[i].t]>d[x]+es[i].w){
 8             d[es[i].t]=d[x]+es[i].w; q.push((nd){d[es[i].t],es[i].t});
 9         }
10     }
11 }

spfa(bfs+slf):

 1 ll d[maxn]; bool inq[maxn]; deque<int>q;
 2 ll spfa(){
 3     inc(i,1,n)d[i]=INF; d[1]=0; inq[1]=1; q.push_back(1);
 4     while(!q.empty()){
 5         int x=q.front(); q.pop_front(); inq[x]=0;
 6         for(int i=g[x];i;i=es[i].n)if(d[es[i].t]>d[x]+es[i].w){
 7             d[es[i].t]=d[x]+es[i].w;
 8             if(!inq[es[i].t]){
 9                 if(!q.empty()&&d[es[i].t]<d[q.front()])q.push_front(es[i].t);else q.push_back(es[i].t);
10                 inq[es[i].t]=1;
11             }
12         }
13     }
14     return d[n];
15 }

spfa(dfs):

 1 bool ins[maxn],f; double d[maxn];
 2 void dfs(int x){
 3     ins[x]=1;
 4     for(int i=g[x];i;i=es[i].n){
 5         if(f)return;
 6         if(d[es[i].t]>d[x]+es[i].w){
 7             if(ins[es[i].t]){f=1; return;} d[es[i].t]=d[x]+es[i].w; dfs(es[i].t);
 8         }
 9     }
10     ins[x]=0;
11 }
12 bool spfa(){
13     memset(ins,0,sizeof(ins)); memset(d,0,sizeof(d)); f=0;
14     inc(i,1,n){dfs(i); if(f)return 0;} return 1;
15 }

floyd:

 1 inc(k,1,n)inc(i,1,n)inc(j,1,n)if(map[i][k]+map[k][j]<map[i][j])map[i][j]=map[i][k]+map[k][j]; 

比较:

dijkstra:

本算法的优点就是快,没有什么一定需要其解决的问题,且只能处理正权边及求最短路(不能求最长路)。复杂度为O(mlog2n)不过常数会稍大。

spfa:

优点是可以处理负权边以及判负环,当不用判负环时bfs+slf(双端队列优化)的最坏复杂度是O(nm),但是常数很小,有时快过dijkstra。当需要判负环时bfs+slf复杂度经常会达到最坏复杂度,这时候用dfs写法会明显快很多,因为在不连通图中判负环需要每个点都作为源点跑一次。

floyd:

优点是短,可以用一行求出两两点的最短路,复杂度O(n3)且常数很小。

常见模型:

差分约束系统

n个数,知道他们两两之间形如ai-aj≥c,ai-aj≤c的关系,求n个数最小/最大是多少。解法:如果是求最大值,则将所有限制条件转化为为≤,将得到的i和j连边,权值为c并跑最短路,因为得到的是满足条件的最大值;如果是求最小值,则将所有限制条件设定为≥,将得到的i和j连边,权值为c并跑最长路,因为得到的是满足条件的最小值。

01分数规划:

n点m边有向图,点有点权,边有边权,从某点出发,走一些路使得经过的点权和除以(浮点数除法)边权和最大或最小,求这个小数(保留两位)。解法:假设需要所求小数最大,那么二分这个数,然后将所有边的边权改为分母(需要最小化的部分)*二分的数-分子(需要最大化的部分),然后判负环。如果有,说明解合法,否则解不合法。最后把下界输出。如果需要所求小数最小,则把边权改为分子(需要最大化的部分)-分母(需要最小化的部分)*二分的数,同时改变范围缩小的方向。

倍增floyd:

从s到t,要求刚好经过k条边,求最短路。解法:把用floyd的方式对两个邻接矩阵进行合并定义成一个运算,则答案为初始矩阵与自己做k次运算得到的结果。而该运算符合结合律,故可以用类似矩阵快速幂的方法刚好做log2k次运算。

原文地址:https://www.cnblogs.com/YuanZiming/p/6058943.html