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

最短路算法大约来说就是有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. nCompass-产品配置基础

    nCompass-产品配置基础 设备上架后,浏览器登陆设备的管理IP,输入用户名和密码, 登入进入视图展示页面 1. 添加许可 新设备上架之后,要添加许可方能使用. 步骤: 系统设置 --- 许可-- ...

  2. #《Essential C++》读书笔记# 第二章 面向过程的编程风格

    基础知识 函数必须先被声明,然后才能被调用(被使用).函数的声明让编译器得以检查后续出现的使用方式是否正确--是否有足够的参数.参数类型是否正确,等等.函数声明不必提供函数体,但必须指明返回类型.函数 ...

  3. 00-django | 01-构建博客目录

    00-django | 01-构建博客目录 python Django 创建blog 进入到 manage.py 文件所在的目录(即项目根目录)下,运行 pipenv run python manag ...

  4. mongo shell

    mongo shell mongo 连接 本地 mongo # 连接127.0.0.1:27017 远程 mongo "mongodb://mongodb0.example.com:2801 ...

  5. javascript 基础整理

    js编码标准 参考 数据类型 注意事项

  6. Redis入门-01

    目录 使用场景 支持的数据类型 主从复制 原理 配置 哨兵机制 持久化 RDB(Redis Database) AOF(Append Only File) redis(Remote DIctionar ...

  7. java 快速生成树的方式

    public class XzqhDto { @ApiModelProperty("另加数据") private String label; @ApiModelProperty(& ...

  8. Django中非视图函数获取用户对象

    今天遇到了一个问题:在Django中怎么从非视图函数中获取用户对象?怎么保证不同的请求获取到不同的用户对象? 平常我们获取用户对象使用的是: request.user 不得不说,这确实很方便. 但是, ...

  9. VS中关于数据库的操作

    1.数据库迁移 第一步: 第二步: 在窗口中选择项目中的EntitiyFramwork项目(与数据库连接的文件集) 第三步: 输入update-database 二:数据对比 第一步: 第二步:选择需 ...

  10. string类型的应用场景 —— Redis实战经验

    string类型是实战中应用最多的数据类型,Redis的一些特性决定了string类型的应用场景. 1. Redis的数据是共享的 如果将用户信息存储在web服务的本地缓存,则每个web服务都会缓存一 ...