• Algorithm Design——最短路径


    本文主要讨论了最短路径的三个算法:Floyd,Dijkstra以及Bellman_Ford的实现方法以及他们之间的区别与联系

      1 /**
      2 Floyd算法流程:
      3       若edge[i][j]表示从结点i到结点j,中间只能经过编号小于k的点时的最短路径长度,
      4 我们可以由这些值确定当中间允许经过编号小于等于k的结点时,它们之间的最短路径长
      5 度。同样,与原情况相比,新情况中允许 出现在中间路径的结点新增了编号为 k 的结
      6 点,同理我们确定 edge[i][k] + edge[k][j]的值与edge[i][j]的值,若前者较小则该值代
      7 表了新情况中从结点i到结 点j的最短路径长度;否则,新情况中该路径长度依旧保持不变。
      8 
      9 Floyd算法注意事项:
     10     (1)时间复杂度,O(N^3),所以在节点个数大于200时,考虑超时问题;
     11     (2)用一个二维矩阵表示图形,如果原图并非由邻接矩阵给出时,设法转换,特别地,
     12 当两个节点之间有多余一条边时,我们选择长度最小的边权值存入邻接矩阵;
     13     (3)放算法完成后,所有节点之间的最短路径都被确定,适合——全源最短路径问题。
     14 
     15 样例输入: 
     16 2 1
     17 1 2 3 
     18 3 3
     19 1 2 5 
     20 2 3 5 
     21 3 1 2
     22 0 0
     23 样例输出:
     24 3 
     25 2
     26 */
     27 
     28 #include<cstdio>
     29 using namespace std;
     30 
     31 int ans[101][101];//二维数组,其初始值即为该图的邻接矩阵
     32 
     33 int main()
     34 {
     35     int n, m;
     36     while(scanf_s("%d%d", &n, &m) != EOF)//输入节点个数以及边个数
     37     {
     38         if(n ==0 && m == 0)
     39             break;
     40 
     41         for(int i = 1 ; i < n ; i ++)
     42         {
     43             for(int j = 0 ; j <=n ; j ++)
     44             {
     45                 ans[i][j] = -1;//对邻接矩阵初始化,-1表示无穷大
     46             }
     47             ans[i][i] = 0;//自己到自己的路径长度设置为0
     48         }
     49 
     50         //输入边信息
     51         while(m --)
     52         {
     53             int a, b, c;
     54             scanf_s("%d%d%d", &a, &b, &c);
     55             ans[a][b] = ans[b][a] = c;//无向图,赋值操作进行两次
     56         }
     57 
     58         for(int k = 1 ; k <= n ; k ++)//k从1到N循环,一次代表允许经过的中间节点编号小于等于k
     59         {
     60             for(int i = 1 ; i <= n ; i ++)
     61             {
     62                 for(int j = 1 ; j <= n ; j ++)//遍历所有的ans[i][j],判断气质保持原值,还是将要被更新
     63                 {
     64                     if(ans[i][k] == -1 || ans[k][j] == -1)//若两值中有一个值为无穷,则ans[i][j]不能经过节点k而被更新,跳过循环,保持原值
     65                         continue;
     66 
     67                     if(ans[i][j] == -1 || ans[i][k] + ans[j][k] < ans[i][j])//当由于经过k可以获得更短的最短路径时,更新该值
     68                         ans[i][j] = ans[i][k] + ans[j][k];
     69                 }
     70             }
     71         }
     72 
     73         printf_s("%d
    ", ans[1][n]);//循环输出最后答案
     74     }
     75 
     76     return 0;
     77 }
     78 
     79 
     80  /**
     81 Dijkstra算法流程:
     82     (1)初始化,集合K中加入节点1,节点1到节点1的最短路径为0,到其他节点为无穷(或不确定);
     83     (2)遍历集合K中与集合K中节点直接相邻的边(U,V,C),其中U属于集合K,V不属于集合K,计算由节点1
     84 出发按照已经得到的最短路到达U,再由U经过该边到达V时的路径长度。比较所有与集合K中节点直接相邻的
     85 非集合K节点该路径长度,其中路径长度最小的节点被确定为下一个最短路径确定的节点,其最短路径长度即为
     86 这个路径长度,最后将该节点加入集合K;
     87 (3)若集合K中已经包含了所有的点,算法结束;否则重复步骤2;
     88 
     89 Dijkstra算法注意事项
     90     (1)单源最短路径问题,即只能计算出某个特定节点到其他所有节点的最短路径长度;
     91     (2)支持邻接链表,在节点比较多时,可以使用这种方法
     92 
     93 样例输入: 
     94 2 1
     95 1 2 3 
     96 3 3
     97 1 2 5 
     98 2 3 5 
     99 3 1 2
    100 0 0
    101 样例输出:
    102 3 
    103 2 
    104 */
    105 
    106 #include<cstdio>
    107 #include<vector>
    108 using namespace std;
    109 
    110 struct E//邻接表中链表原色结构体(由一个节点所持有)
    111 {
    112     int next;//代表直接相邻的点
    113     int c;//代表该边的权重
    114 };
    115 
    116 vector<E> edge[101];//邻接表
    117 bool mark[101];//标记,当mark[j]为true时,表示节点j的最短路径长度已经得到,该节点已经加入集合K
    118 int Dis[101];//距离向量,当mark[k]为true时,表示已经得到最短路径;
    119                 //否则表示所有从节点1出发,经过已知的最短路径达到集合K中的某点,在经过一条边到达节点i的路径中最短距离
    120 
    121 int main()
    122 {
    123     int n , m;
    124 
    125     while(scanf_s("%d%d", &n, &m) != EOF)//节点以及边个数
    126     {
    127         if(n == 0 && m ==0)
    128             break;
    129 
    130         for(int i = 1 ; i <= n ; i ++)
    131             edge[i].clear();//清空上一次数据
    132 
    133         while(m --)
    134         {
    135             int a, b, c;//节点a,节点b,权重c
    136             scanf_s("%d%d%d", &a, &b, &c);
    137 
    138             E tmp;
    139             tmp.c = c;
    140             tmp.next = b;
    141             edge[a].push_back(tmp);
    142             tmp.next = a;
    143             edge[b].push_back(tmp);//将邻接信息加入邻接表,由于是无向图,从而每条边信息都要添加到其链各个两个顶点的两条单链表中
    144         }
    145 
    146         for(int i = 1 ; i <= n ; i ++)//初始化
    147         {
    148             Dis[i] = -1;//所有距离为-1,即不可达
    149             mark[i] = false;//所有节点不属于集合K
    150         }
    151 
    152         Dis[1] = 0;//得到最近的点为节点1,长度为0
    153         mark[1] = true;//将节点1加入集合K
    154         int newP = 1;//集合K中新加入的点为节点1
    155         for(int i = 1 ; i < n ; i ++)//循环n - 1次,按照最短路径递增的顺序确定其他n - 1个点的最短路径长度
    156         {
    157             for(int j = 0 ; j < edge[newP].size() ; j++)//遍历与该新加入集合K中的节点直接相邻的边
    158             {
    159                 int t = edge[newP][j].next;//该边的下一个节点
    160                 int c = edge[newP][j].c;//该边的权重
    161 
    162                 if(mark[t] == true)//若另一个节点也属于集合K,则跳过
    163                     continue ;
    164 
    165                 if(Dis[t] == -1 || Dis[t] > Dis[newP] + c)//若该节点尚不可达或者该节点新加入的节点经过一条边到达时比以往距离更短
    166                     Dis[t] = Dis[newP] + c;//更新距离信息
    167             }
    168 
    169             int min = 123123123;//最小值初始化为一个大整数,为找最小值做准备
    170             for(int j = 1 ; j <= n ; j ++)//遍历所有节点
    171             {
    172                 if(mark[j] == true)//若其属于集合K,则跳过
    173                     continue ;
    174                 if(Dis[j] == -1)//若该节点仍不可达,则跳过
    175                     continue ;
    176                 if(Dis[j] < min)//若该节点经由节点1到集合K的某点在经过一条边到达时距离小于当前最小值
    177                 {
    178                     min = Dis[j];//更新其为最小值
    179                     newP = j;//新加入的点,暂定为该节点
    180                 }
    181             }
    182             mark[newP] = true;//将新加入的点加入集合K,Dis[newP]虽然数值不变,但意义发生变化,由所有经过集合K中的节点在经过一条边到达时的距离中的最小值变为从节点1到节点newP的最短距离
    183         }
    184 
    185         printf_s("%d
    ", Dis[n]);//输出
    186     }
    187 
    188     return 0;
    189 }
    190 
    191 /**
    192 Dijkstra算法流程:
    193 (1)初始化,集合K中加入节点1,节点1到节点1的最短路径为0,到其他节点为无穷(或不确定);
    194 (2)遍历集合K中与集合K中节点直接相邻的边(U,V,C),其中U属于集合K,V不属于集合K,计算由节点1
    195 出发按照已经得到的最短路到达U,再由U经过该边到达V时的路径长度。比较所有与集合K中节点直接相邻的
    196 非集合K节点该路径长度,其中路径长度最小的节点被确定为下一个最短路径确定的节点,其最短路径长度即为
    197 这个路径长度,最后将该节点加入集合K;
    198 (3)若集合K中已经包含了所有的点,算法结束;否则重复步骤2;
    199 
    200 Dijkstra算法注意事项
    201 (1)单源最短路径问题,即只能计算出某个特定节点到其他所有节点的最短路径长度;
    202 (2)支持邻接链表,在节点比较多时,可以使用这种方法
    203 
    204 样例输入:
    205 4 6 1
    206 1 2 20
    207 1 3 5
    208 4 1 -200
    209 2 4 4
    210 4 2 4
    211 3 4 2
    212 4 6 1
    213 1 2 2
    214 1 3 5
    215 4 1 10
    216 2 4 4
    217 4 2 4
    218 3 4 2
    219 
    220 样例输出:
    221 have negative circles
    222 0
    223 2
    224 5
    225 6
    226 */
    227 
    228 
    229 #include <iostream>
    230 using namespace std;
    231 const int maxnum = 100;
    232 const int maxint = 99999;
    233 
    234 // 边,
    235 typedef struct Edge{
    236     int u, v;    // 起点,重点
    237     int weight;  // 边的权值
    238 }Edge;
    239 
    240 Edge edge[maxnum];     // 保存边的值
    241 int  dist[maxnum];     // 结点到源点最小距离
    242 
    243 int nodenum, edgenum, source;    // 结点数,边数,源点
    244 
    245 // 初始化图
    246 void init()
    247 {
    248     // 输入结点数,边数,源点
    249     cin >> nodenum >> edgenum >> source;
    250     for(int i = 1 ; i <= nodenum ; ++i)
    251         dist[i] = maxint;
    252     dist[source] = 0;
    253     for(int i = 1 ; i <= edgenum ; ++i)
    254     {
    255         cin >> edge[i].u >> edge[i].v >> edge[i].weight;//输入边以及权重
    256         if(edge[i].u == source)          //注意这里设置初始情况
    257             dist[edge[i].v] = edge[i].weight;
    258     }
    259 }
    260 
    261 // 松弛计算
    262 void relax(int u, int v, int weight)
    263 {
    264     if(dist[v] > dist[u] + weight)
    265         dist[v] = dist[u] + weight;
    266 }
    267 
    268 bool Bellman_Ford()
    269 {
    270     for(int i = 1; i <= nodenum - 1; ++i)
    271         for(int j = 1 ; j <= edgenum ; ++j)
    272             relax(edge[j].u, edge[j].v, edge[j].weight);
    273     bool flag = 1;
    274     // 判断是否有负环路
    275     for(int i = 1; i <= edgenum; ++i)
    276         if(dist[edge[i].v] > dist[edge[i].u] + edge[i].weight)
    277         {
    278             flag = 0;
    279             break;
    280         }
    281         return flag;
    282 }
    283 
    284 int main()
    285 {
    286     init();
    287 
    288     if(Bellman_Ford())
    289     {
    290         for(int i = 1 ;i <= nodenum; i++)
    291             cout << dist[i] << endl;
    292     }
    293     else
    294         printf_s("have negative circles
    ");
    295     return 0;
    296 }
  • 相关阅读:
    三剑客之grep命令
    expect
    信号控制
    数组
    LaTex: Cetx +Winedit之文献引用---Elsevier模板
    vue系列--【animate.css、过滤器、组件基础】
    vue系列--【生命周期、侦听器watch、计算属性、jsonp解决跨域】
    vue系列--【动态样式、表单数据绑定、表单修饰符、事件处理、$set】
    vue系列--【vue核心、vue实例、指令】
    node系列--【socket.io框架】
  • 原文地址:https://www.cnblogs.com/yiyi-xuechen/p/3452298.html
Copyright © 2020-2023  润新知