当然,这篇文章是借鉴大佬的。。。

最短路算法大约来说就是有4种——Dijkstra,Floyd,Bellman_Ford,SPFA

接下来,就可以一一看一下。。。

1.Dijkstra(权值非负,适用于有向图及无向图,单源最短路)

1 Dijkstra's算法解决的是图中单个源点到其它顶点的最短路径。只能解决权值非负(看了代码就知道了)
2 Dijkstral只能求出任意点到达源点的最短距离(不能求出任意两点之间的最短距离),同时适用于有向图和无向图,复杂度为O(n^2).
3算法的过程: 
1设置顶点集合S并不断的作贪心选择来选择扩充这个集合。一个顶点属于集合S当且仅当从源点到该点的最短路径长度已知
2 初始时,S中仅含有源。设U是G的某一个顶点,把从源到U且中间只经过S中的顶点的路称为从源到U的特殊路径,并用dis数组距离当前每一个顶点所对应的最短特殊路径
3Dijkstra算法每一次从V-S中取出具有最短特殊长度的顶点u,将u添加到S中,同时对dis数组进行修改。一旦S包含了所有的V中的顶点,dis数组就记录了从源点到其它顶点的最短路径长度。
4 模板:
没有优化,时间复杂度o(n^2)

 #define MAXN 1010
 #define INF 0xFFFFFFF
 int  value[MAXN][MAXN];/*保存的是边权值*/
 int  dis[MAXN];/*保存源点到任意点之间的最短路*/
 int  father[MAXN];/*保存i点的父亲节点*/
 int  vis[MAXN];/*记录顶点是否没取过*/ 

 void input(){
       int star , end , v;
       scanf("%d%d" , &n , &m);
       /*初始化value数组*/
       ; i <= n ; i++){
          ; j <= n ; j++)
              value[i][j] = INF;
      }
      ; i < m ; i++){
         scanf("%d%d%d" ,  &star , &end , &v);
         if(value[star][end] == INF)
             value[star][end] = value[end][star] = v;/*处理成无向图*/
         else{
             if(v < value[star][end])/*判断重边是否出现*/
                  value[star][end] = value[end][star] = v;
         }
 }

 void dijkstra(int s){
      memset(vis ,  , sizeof(vis));
      memset(father ,  , sizeof(father));
      /*初始化dis数组*/
       ; i<= n ; i++)
           dis[i] = INF;
      dis[s] = ;
       ; i <= n ; i++){/*枚举n个顶点*/
         int pos;
         pos = -;
          ; j <= n ;j++){/*找到未加入集合的最短路点*/
             || dis[j] < dis[pos]))
               pos = j;
         }
         vis[pos] = ;/*把这个点加入最短路径集合*/
          ; j <= n ; j++){/*更新dis数组*/
            if(!vis[j] && (dis[j] > dis[pos] + value[pos][j])){
              dis[j] = dis[pos] + value[pos][j];
              father[j] = pos;
            }
         }
      }
 }

当然,肯定是得有优化过的。。。时间复杂度o(mlogn),这里用的是邻接表优化,当然最自然还是得属链式前向星。

 优化过的,时间复杂度为o(mlogn);
 /*利用邻接表来优化*/
 #include<utility>
 typedef pair<int , int>pii;/*pair专门把两个类型捆绑在一起的*/
 priority_queue<pii,vector<pii>,greater<pii> >q;/*优先队列默认使用“<”,那么优先队列的元素是从大到小,所以自己定义“>”比较,STL中可以用greater<int>来表示">",这样就可以来声明一个小整数先出队的优先队列*/
 #define MAXN 1010
 #define INF 0xFFFFFFF
 int n , m;/*有n个点,m条边*/
 int first[MAXN] , next[MAXN];/*first数组保存的是节点i的第一条边,next保存的是边e的下一条边*/
 int u[MAXN] , v[MAXN] , value[MAXN];
 int vis[MAXN];
 int dis[MAXN];

 /*读入这个图*/
 void input(){
      scanf("%d%d" , &n , &m);
      /*初始化表头*/
       ; i <= n ; i++)
         first[i] = -;
       ; i <= m ; i++){
         scanf("%d%d" , &u[i] , &v[i] , &value[i]);
         next[i] = first[u[i]];/*表头往后移动*/
         first[u[i]] = i;/*更新表头*/
      }
 }

 /*Dijkstra*/
 void Dijkstra(int s){
      memset(vis ,  , sizeof(vis));
      /*初始化点的距离*/
       ; i <= n ; i++)
           dis[i] = INF;
     dis[s] = ;
      while(!q.empty())
         q.pop();
      q.push(make_pair(dis[s] , s));
      while(!q.empty()){
           pii u = q.top();
           q.pop();
           int x = u.second;
           if(vis[x])
             continue;
           vis[x] = ;
            ; i = next[i]){
              if(dis[v[e]] > dis[x] + value[i]){
                 dis[v[i]] = dis[x] + value[i];
                 q.push(make_pair(dis[v[i] , v[i]));
              }
           }
      }
 }

2.Floyd(权值非负,适用于有向图及无向图,任意两点最短路)

1 floyd 的思想就是通过枚举n个点利用DP的思想来更新最短距离的,假设当前枚举到第k个点,那么就有任意的两个点i , j ,如果i k 相连 j k 相连 那么就可以知道这个时候dis[i][j] = min(dis[i][j] , dis[i][k] + dis[k][j]);,那么只要枚举完n个点,那么就说明已经完全更新完所有两点直间的最短路。

2 floyd算法是最简单的最短路径的算法,可以计算图中任意两点间的最短路径。floyd算法的时间复杂度为o(n^3),如果是一个没有边权的图,把相连的两点间的距离设为dis[i][j]=1.不相连的两点设为无穷大,用floyd算法可以判断i j两点是否相连。
3 floyd 算法不允许所有的权值为负的回路。可以求出任意两点之间的最短距离。处理的是无向图
4 缺点是时间复杂度比较高,不适合计算大量数据

5 如果dis[i][i] != 0,说明此时存在环。

6 如果利用floyd求最小值的时候,初始化dis为INF , 如果是求最大值的话初始化为-1.

7 模板:时间复杂度o(n^3)

 #define INF 0xFFFFFFF
 #define MAXN 1010
 int dis[MAXN][MAXN];

 /*如果是求最小值的话,初始化为INF即可*/
 void init(){
       ; i <= n ; i++){
          ; j <= n ; j++)
              dis[i][j] = INF;
         dis[i][i] = ;
      }
 }

 /*如果是求最大值的话,初始化为-1*/
 void init(){
       ; i <= n ; i++){
          ; j <= n ; j++)
              dis[i][j] = -;
      }
 }

 /*floyd算法*/
 void folyd(){
          ; k <= n ; k++){/*枚举n个点来更新dis*/
              ; i <= n ; i++){
                  ; j <= n ; j++)
                    && dis[j][k] != -)/*如果在求最大值的时候加上这一句*/
                      dis[i][j] = min(dis[i][k]+dis[k][j] , dis[i][j]);
             }
        }
  }

当然,一般的最短路应该没有那么得emmZZ优秀,肯定还是得有一个扩展来求最短路径的 所以。。。

如何用floyd找出最短路径所行经的点: 1 这里要用到另一个矩阵P,它的定义是这样的:p(ij)的值如果为p,就表示i到j的最短行经为i->p...->j,也就是说p是i到j的最短行径中的j之前的第1个点。

2 P矩阵的初值为p(ij) = j。有了这个矩阵之后,要找最短路径就轻而易举了。对于i到j而言找出p(ij),令为p,就知道了路径i->p....->j;再去找p(pj),如果值为q,p到j的最短路径为p->q...->j;再去找p(qj),如果值为r,i到q的最短路径为q>r...->q;所以一再反复,就会得到答案。

3  但是,如何动态的回填P矩阵的值呢?回想一下,当d(ij)>d(ik)+d(kj)时,就要让i到j的最短路径改为走i->...->k->...->j这一条路,但是d(ik)的值是已知的,换句话说,就是i->...->k这条路是已知的,所以i->...->k这条路上k的第一个城市(即p(ik))也是已知的,当然,因为要改走i->...->k->...->j这一条路,p(ij)的第一个城市正好是p(ik)。所以一旦发现d(ij)>d(ik)+d(kj),就把p(ik)存入p(ij).

4 代码:

 int dis[MAXN][MAXN];
 int path[MAXN][MAXN];

 void floyd(){
       int  i, j, k;
       /*先初始化化为j*/
       ; i <= n; i++){
            ; j <= n; j++)
                path[i][j] = j;
       }
       ; k <= n; k++){/*枚举n个点*/
           ; i <= n; i++){
               ; j <= n; j++){
                    if (dis[i][j] > dis[i][k]+dis[k][j]){
                          path[i][j] = path[i][k];/*更新为path[i][k]*/
                          dis[i][j] = dis[i][k]+dis[k][j];
                     }
               }
            }
       }
 }

牛逼的是,Floyd还是可以求最小环的,但本蒟蒻还是比较稀饭用并查集求最小环

1 为什么要在更新最短路之前求最小环:
在第k层循环,我们要找的是最大结点为k的环,而此时Dist数组存放的是k-1层循环结束时的经过k-1结点的最短路径,也就是说以上求出的最短路是不经过k点的,这就刚好符合我们的要求。为什么呢?假设环中结点i,j是与k直接相连,如果先求出经过k的最短路,那么会有这样一种情况,即:i到j的最短路经过k。这样的话就形成不了环 。

2最小环改进算法的证明:
一个环中的最大结点为k(编号最大),与他相连的两个点为i,j,这个环的最短长度为g[i][k]+g[k][j]+dis[i][j] (i到j的路径中,所有结点编号都小于k的最短路径长度)。根据floyd的原理,在最外层循环做了k-1次之后,dist[i][j]则代表了i到j的路径中,所有结点编号都小于k的最短路径, 综上所述,该算法一定能找到图中最小环。

3 为什么还要value数组:
因为dis数组时刻都在变动不能表示出原来两个点之间的距离。

4 形成环至少要有3点不同的点,两个点是不能算环的。
5 代码:

 int mincircle = INF;
 int dis[MAXN][MAXN];
 int value[MAXN][MAXN];

 void floyd(){
      memcpy(value , dis , sizeof(value));
       ; k <= n ; k++){
           /*求最小环,不包含第k个点*/
            ; i < k ; i++){/*到k-1即可*/
                 ; j < k ; j++)/*到k-1即可*/
                    mincircle = min(mincircle , dis[i][j]+value[i][k]+value[k][j]);/*无向图*/
                    mincircle = min(mincircle , dis[i][j] +value[i][k]+value[k][j]);/*表示i->k , k->j有边*/
            }
           /*更新最短路*/
            ; i <= n ; i++){
                 ; j <= n ; j++)
                    dis[i][j] = min(dis[i][k]+dis[k][j] , dis[i][j]);
           }
      }
 }

3.Bellman_Ford(权值可正可负,用来判断负环,有向图与无向图,单源最短路)

1 Bellman_Frod可以计算边权为负值的最短路问题,适用于有向图和无向图.用来求解源点到达任意点的最短路。

2 在边权可正可负的图中,环有零环,正环,负环3种。如果包含零环和正环的话,去掉以后路径不会变成,如果包含负环则最短路是不存在的。那么既然不包含负环,所以最短路除了源点意外最多只经过n-1个点,这样就可以通过n-1次的松弛得到源点到每个点的最短路径。

3 时间复杂度o(n*m);
4 如果存在环的话就是经过n-1次松弛操作后还能更新dis数组。
5 模板:

 #define INF 0xFFFFFFF
 #define MAXN 1010*2
 int dis[MAXN];/*dis[i]表示的是源点到点i的最短路*/
 strucu Edge{
    int x;
    int y;
    int value;
 }e[MAXN];

 /*返回最小值*/
 int min(int a , int b){
     return a < b ? a : b;
 }

 /*处理成无向图*/
 void input(){
       ; i < m ; i++){
         scanf("%d%d%d" , e[i].x , &e[i].y , &e[i].value);
         e[i+m].x = e[i].y;/*注意地方*/
         e[i+m].y = e[i].x;/*注意地方*/
         e[i+m].value = e[i].value;
      }
 }

 /*假设现在有n个点,m条边*/
 void Bellman_Ford(int s){
     /*初始化dis数组*/
      ; i <= s ; i++)
          dis[i] = INF;
     dis[s] = ;
      ; i < n ; i++){/*做n-1次松弛*/
         ; j < *m ; j++){/*每一次枚举2*m条边,因为是无向图*/
           if(dis[e[j].y] > dis[e[j].x] + e[j].value)
            dis[e[j.].y] = dis[e[i].x]+e[j].value;/*更新dis[e[j].y]*/
           }
        }
     }
 }

4.SPFA(权值可正可负,判定负环,有向图和无向图,单源最短路)

本蒟蒻还是比较稀饭SPFA的,至少是Bellman_Frod的升级版,并且lll优化和slf优化使得其非常牛X优秀

 #define MAXN 1010
 #define INF 0xFFFFFFF

 int n , m;
 int first[MAXN] , next[MAXN];
 int star[MAXN] , end[MAXN] , value[MAXN];
 int dis[MAXN];
 queue<int>q;

 /*输入*/
 void input(){
      scanf("%d%d" , &n , &m);
      /*初始化表头*/
       ; i <= n ; i++){
         first[i] = -;
         next[i] = -;
      }
      /*读入m条边*/
       ; i < m ; i++){
         scanf("%d%d%d" , &star[i] , &end[i] , &value[i]);
         star[i+m] = end[i];
         end[i+m] = star[i];
         value[i+m] = value[i];

         next[i] = first[star[i]];/*由于要插入表头,所以将原先表头后移*/
         first[star[i]] = i;/*插入表头*/
         next[i+m] = first[star[i+m]];
         first[star[i+m]] = i+m;
      }
 }

 /*SPFA*/
 void SPFA(int s){
      while(!q.empty())
           q.pop();
      int vis[MAXN];
      memset(vis ,  , sizeof(vis));
      /*初始化dis*/
       ; i <= n ; i++)
           dis[i] = INF;
      dis[s] = ;
      q.push(s);/*将源点加入队列*/
      vis[s] = ;/*源点标记为1*/
      while(!q.empty()){
           int x = q.front();
           q.pop();
           vis[x] = ;/*注意这里要重新标记为0,说明已经出队*/
           /*枚举和点x有关的所有边*/
            ; i = next[i]){
              if(dis[end[i]] > dis[x] + value[i]){/*松弛操作,利用三角不等式*/
                dis[end[i]] = dis[x] + value[i];
                if(!vis[end[i]]){/*如果该点不再队列里面*/
                   vis[end[i]] = ;
                   q.push(end[i]);
                }
              }
           }
      }

既然已经在前面提到过SPFA的两种优化方法了,这里就来YY介绍一下

SLF优化(双端队列deque优化):Small Label First 策略:设要加入的节点是j jj,队首元素为i ii,若dist(j)&lt;dist(i) dist(j) &lt; dist(i)dist(j)<dist(i),则将j插入队首,否则插入队尾。(deque)
LLL优化:Large Label Last 策略:设队首元素为i ii,每次弹出时进行判断,队列中所有dist值的平均值为x,若dist(i)>x dist(i)&gt;xdist(i)>x则将i ii插入到队尾,查找下一元素,直到找到某一i ii使得dist(i)&lt;=x dist(i)&lt;=xdist(i)<=x,则将i出对进行松弛操作。

 char str[maxn][maxn];
 int vis[maxn][maxn],dis[maxn][maxn],n,m;
 ],sum,cnt;
 ,-,,,,,,-},dy[] = {,,,,,-,-,-};
 deque<PII>q;
 void spfa()
 {
     while(!q.empty()){
         PII f = q.front();q.pop_front();
         //LLL优化
         if(dis[f.fi][f.se] * cnt > sum){
             q.push_back(f);
             continue;
         }
         sum -= dis[f.fi][f.se];cnt--;
         vis[f.fi][f.se] = ;
         ; i < ; i++ ){
             int nx = f.fi + dx[i],ny = f.se + dy[i];
              || nx > n || ny <  || ny > m)continue;
             int w = (str[nx][ny] != str[f.fi][f.se]);
             if(dis[nx][ny] > dis[f.fi][f.se] + w){
                 dis[nx][ny] = dis[f.fi][f.se] + w;
                 if(!vis[nx][ny]){
                     vis[nx][ny] = ;
                     //SLF优化
                     if(dis[nx][ny] < dis[q.front().fi][q.front().se]){
                         q.push_front(mp(nx,ny));
                     }
                     else {
                         q.push_back(mp(nx,ny));
                     }
                     sum += dis[nx][ny];cnt++;
                 }
             }
         }
     }
 }
 void init()
 {
     cl(dis,INF);
     cl(vis,);
     cl(ans,INF);
     sum = cnt = ;
 }

本蒟蒻还是比较稀饭slf优化的(主要是习惯了)

最短路(Dijkstra,Floyd,Bellman_Ford,SPFA)的更多相关文章

  1. HDU 2544 最短路(floyd+bellman-ford+spfa+dijkstra队列优化)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2544 题目大意:找点1到点n的最短路(无向图) 练一下最短路... dijkstra+队列优化: #i ...

  2. poj1847 Tram(Dijkstra || Floyd || SPFA)

    题目链接 http://poj.org/problem?id=1847 题意 有n个车站,编号1~n,每个车站有k个出口,车站的出口默认是k个出口中的第一个,如果不想从默认出口出站,则需要手动选择出站 ...

  3. hdu-2544-最短路(dijkstra算法模板)

    题目链接 题意很清晰,入门级题目,适合各种模板,可用dijkstra, floyd, Bellman-ford, spfa Dijkstra链接 Floyd链接 Bellman-Ford链接 SPFA ...

  4. 最短路知识点总结(Dijkstra,Floyd,SPFA,Bellman-Ford)

    Dijkstra算法: 解决的问题: 带权重的有向图上单源最短路径问题.且权重都为非负值.如果采用的实现方法合适,Dijkstra运行时间要低于Bellman-Ford算法. 思路: 如果存在一条从i ...

  5. 最短路径-Dijkstra+Floyd+Spfa

    Dijkstra算法: Dijkstra(迪杰斯特拉)算法是典型的单源最短路径算法,用于计算一个节点到其他所有节点的最短路径.主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止.Dijkstra ...

  6. 算法学习笔记(三) 最短路 Dijkstra 和 Floyd 算法

    图论中一个经典问题就是求最短路.最为基础和最为经典的算法莫过于 Dijkstra 和 Floyd 算法,一个是贪心算法,一个是动态规划.这也是算法中的两大经典代表.用一个简单图在纸上一步一步演算,也是 ...

  7. 【最短路算法】Dijkstra+heap和SPFA的区别

    单源最短路问题(SSSP)常用的算法有Dijkstra,Bellman-Ford,这两个算法进行优化,就有了Dijkstra+heap.SPFA(Shortest Path Faster Algori ...

  8. 最短路问题(Bellman/Dijkstra/Floyd)

    最短路问题(Bellman/Dijkstra/Floyd) 寒假了,继续学习停滞了许久的算法.接着从图论开始看起,之前觉得超级难的最短路问题,经过两天的苦读,终于算是有所收获.把自己的理解记录下来,可 ...

  9. 1.1.1最短路(Floyd、Dijstra、BellmanFord)

    转载自hr_whisper大佬的博客 [ 一.Dijkstra 比较详细的迪杰斯特拉算法讲解传送门 Dijkstra单源最短路算法,即计算从起点出发到每个点的最短路.所以Dijkstra常常作为其他算 ...

  10. 算法专题 | 10行代码实现的最短路算法——Bellman-ford与SPFA

    今天是算法数据结构专题的第33篇文章,我们一起来聊聊最短路问题. 最短路问题也属于图论算法之一,解决的是在一张有向图当中点与点之间的最短距离问题.最短路算法有很多,比较常用的有bellman-ford ...

随机推荐

  1. netsh 查看自己的wifi密码。

    查看自己曾经连接过得wifi netsh wlan show profiles 断开wifi netsh wlan disconnect 查看密码 netsh wlan show profile na ...

  2. No mapping found for HTTP request with URI [/SLSaleSystem/js/jquery.dataTables.min.js] in DispatcherServlet with name 'spring' 静态资源文件访问不到,无解!!!!!!!

    报错信息:   网上三种修改 web.xml 文件方法尝试未果 尝试未果:<mvc:default-servlet-handler/> 尝试未果:方法2:直接告诉spring,这个你就得这 ...

  3. kuangbin专题 专题九 连通图 Warm up HDU - 4612

    题目链接:https://vjudge.net/problem/HDU-4612 题目:一个大地图,给定若干个连通图,每个连通图中有若干个桥,你可以在任意某个连通图的 任意两个点添加一条边,问,添加一 ...

  4. 树莓派搭载CentOS7系统初始配置

    系统属性: 树莓派型号:3b SD:32GB 系统:CentOS-Userland-7-armv7hl-RaspberryPI-Minimal-1908-sda.raw 开机配置: 连接树莓派: 配件 ...

  5. SSH-Secure-Shell 3.2.9 build283版本,创建直接打开文件传输的快捷方式的方法

    2019-12-31 16:21:23 版本信息: 在安装目录下新建快捷方式 目标填写:"D:\SSH-Secure-Shell\SshClient.exe" /f 图标选择,系统 ...

  6. ArcGIS Runtime SDK for Android中SimpleFillSymbol.Style样式

    SimpleFillSymbol.Style样式枚举共8种: 1.BACKWARD_DIAGONAL 反对角线填充 2.CROSS 交叉线填充 3.DIAGONAL_CROSS 前后对角线填充 4.F ...

  7. Python 编程入门(4):变量与赋值

    以下所有例子都基于最新版本的 Python,为了便于消化,每一篇都尽量短小精悍,希望你能尽力去掌握 Python 编程的「概念」,可以的话去动手试一下这些例子(就算目前还没完全搞懂),加深理解. 经过 ...

  8. Mac 终端 Tomcat 环境配置过程

    Tomcat是Apache 软件基金会(Apache Software Foundation)的Jakarta 项目中的一个核心项目,由Apache.Sun 和其他一些公司及个人共同开发而成.Tomc ...

  9. Linux命令详解之–chmod命令

    在Linux中,一般使用chmod命令来修改文件的属性. 利用 chmod 可以藉以控制文件如何被他人所调用.此命令所有使用者都可使用. 一.Linux chmod命令语法Linux chmod 命令 ...

  10. 2019年IT事故盘点【IT必读】

    昀哥@老兵笔记 2020农历新年开局不容易,新冠肺炎仍在攻艰克难阶段.回首过去的9102年,总有一些事主要是事故值得去记录.下面我们来盘点一下9102年的“外部事故”. 一,我们遭遇的IT基础设施服务 ...