zoukankan      html  css  js  c++  java
  • 经典的多源最短路径算法——Floyd

      Floyd算法是经典的求算多源最短路径的算法,它的实质还是一种动态规划思想的应用。
     
    一、Floyd算法的实现思想

    Floyd算法是如何实现的呢,我下面做简单说明:
      我们要求算i,j两点间的最短距离,首先我们引入一个中间点k,看看从i到j有没有一条经过k的通路(即i→k→j),如果有这么一条路,那么我们将目前的从i到j的距离,与从i到k再到j的距离相比较,小的那一个更新为新的从i到j的最短路。
    那么用dp写出它的状态转移方程有:
      
    那么在代码里我们要怎样来实现呢?
    首先需要一个矩阵来储存各个点的关系,对于下面一个图,我们可以得到相应的关系矩阵(数字代表由i到j的路径长度,INF代表目前无法连通):

    其中第i行第j列就代表从点i到点j的距离。
    然后我们需要三层循环,最外层k遍历中间点,里面为i,j遍历矩阵,结合状态转移方程得到Floyd算法的核心代码:
     1 void floyd()
     2 {
     3     int i,j,k;
     4     for(k=1;k<=n;k++)
     5         for(i=1;i<=n;i++)
     6             for(j=1;j<=n;j++)
     7             {
     8                 if(dp[i][j]>dp[i][k]+dp[k][j])
     9                     dp[i][j]=dp[i][k]+dp[k][j];
    10             }
    11 }

    显然我们可以从代码里看出,该算法的时间复杂度为O()。

    下面我们用上面的例子来模拟一下。
    首先令k=1,开始遍历i,j,对于第(i,j)个关系来说,我们要把它本身的值和第i行第k个、第j列第k个的值的和相比较,取小的作为新的值。如下图
    对于第2行第5列的值来说,它的新的值是第2行第1个值和第5列第1个值的和。按照同样的方法我们把k=1的遍历走完
    然后我们令k=2再开始遍历
    同理,对于第1行第3列的值来说,它的新的值是第1行第2个值和第3列第2个值的和。然后我们把k=2的情况遍历完
    然后我们接着把k=3,4,5…全部遍历完,得到我们的最短路的矩阵
    Floyd算法可以求解大多数情况,但是注意Floyd算法无法求解有负权回路的状况,如
    对于这个回路来说,它每进行一次循环,最短路就会减少1,所以永远也找不到最短路。
     
    二、Floyd算法的正确性
    (施工中orz)
     
    三、Floyd算法的其他应用
     
    1.求解传递闭包
    将Floyd算法稍作修改便可以得到求解传递闭包的代码:
    1 void floyd()
    2 {
    3     int i,j,k;
    4     for(k=1;k<=n;k++) 
    5         for(i=1;i<=n;i++)
    6             for(j=1;j<=n;j++)
    7                 if(matrix[i][k]&&matrix[k][j])
    8                     matrix[i][j]=1;
    9 }

    若有会Warshall算法求传递闭包的朋友便会发现Floyd算法和Warshall算法高度相似!

    相关题目:ZOJ P4124
    代码:
     1 #include <bits/stdc++.h>
     2 
     3 using namespace std;
     4 
     5 int n,m,matrix[105][105],num[2][105],flag=0;
     6 
     7 void floyd()
     8 {
     9     int i,j,k;
    10 
    11     for(k=1;k<=n;k++)      //floyd
    12         for(i=1;i<=n;i++)
    13             for(j=1;j<=n;j++)
    14                 if(matrix[i][k]&&matrix[k][j])
    15                     matrix[i][j]=1;
    16 
    17     for(i=1;i<=n;i++)      //判断是否有自环
    18         for(j=1;j<=n;j++)
    19             if(matrix[i][j]&&matrix[j][i])
    20             {
    21                 flag=1;
    22                 return;
    23             }
    24 
    25     for(i=1;i<=n;i++)    //维护更新num数组
    26         for(j=1;j<=n;j++)
    27             if(matrix[i][j])
    28             {
    29                 num[0][i]++;
    30                 num[1][j]++;
    31             }
    32 }
    33 
    34 int main()
    35 {
    36     int t;
    37     scanf("%d",&t);
    38     while(t--)
    39     {
    40         int i,a,b;
    41 
    42         memset(matrix,0,sizeof(matrix));    //初始化
    43         memset(num,0,sizeof(num));
    44         flag=0;
    45 
    46         scanf("%d%d",&n,&m);
    47         for(i=0;i<m;i++)    //预处理
    48         {
    49             scanf("%d%d",&a,&b);
    50             matrix[a][b]=1;
    51         }
    52         floyd();
    53         if(flag==0)    //输出,当一个数比它大的和比它小的都小于n/2时,可视为该项为中间项
    54         {
    55             for(i=1;i<=n;i++)
    56             {
    57                 if(num[0][i]<=n/2&&num[1][i]<=n/2) printf("1");
    58                 else printf("0");
    59             }
    60             printf("
    ");
    61         }
    62         else
    63         {
    64             for(i=0;i<n;i++) printf("0");
    65             printf("
    ");
    66         }
    67     }
    68     return 0;
    69 }
    ZOJ P4124

    (用Floyd算法解此题算是一个比较巧妙的解法)

    四、相关题目

    1.例题 Luogu P2910

    代码:

     1 #include <bits/stdc++.h>
     2 
     3 using namespace std;
     4 
     5 int matrix[105][105],n;
     6 
     7 void floyd()
     8 {
     9     int i,j,k;
    10     for(k=1;k<=n;k++)
    11         for(i=1;i<=n;i++)
    12             for(j=1;j<=n;j++)
    13             {
    14                 matrix[i][j]=min(matrix[i][j],matrix[i][k]+matrix[k][j]);
    15             }
    16 }
    17 
    18 
    19 int main()
    20 {
    21     int i,j,m,order[10005]={0},ans=0;
    22     scanf("%d%d",&n,&m);
    23     for(i=0;i<m;i++) scanf("%d",&order[i]);
    24     for(i=1;i<=n;i++)
    25         for(j=1;j<=n;j++) scanf("%d",&matrix[i][j]);
    26     floyd();
    27     for(i=0;i<m-1;i++)
    28         ans+=matrix[order[i]][order[i+1]];
    29     printf("%d",ans);
    30     return 0;
    31 }
    Luogu P2910

    Author : Houge  Date : 2019.6.1

    Update log : 

  • 相关阅读:
    有效获取状态栏(StatusBar)高度
    各种数据库连接
    Spring AOP 原理
    Spring ioc 原理
    转载的别人的ajax跨域解决方法
    如何监听input的脚本赋值
    JAVA之Socket编程
    JAVA中的多线程
    JAVA文件操作
    实验三 kali下metasploit的漏洞攻击实践
  • 原文地址:https://www.cnblogs.com/CSGOBESTGAMEEVER/p/10936333.html
Copyright © 2011-2022 走看看