最短路(floyd)

算法思想

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

算法特点

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

3.缺点是时间复杂度比较高,不适合计算大量数据

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

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

void init()
{  
    for(int i = 1 ; i <= n ; i++)
    {  
        for(int j = 1 ; j <= n ; j++)  
             dis[i][j] = (i==j) ? INF : 0;  //求最小值
             //dis[i][j] = -1;//求最大值
    }  
}  

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


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).

int dis[maxn][maxn];
int path[maxn][maxn];

void floyd()
{
    for(int i = 1; i <= n; i++)/*先初始化化为j*/
    {
        for(j = 1; j <= n; j++)
            path[i][j] = j;
    }
    for(int k = 1; k <= n; k++)/*枚举n个点*/
    {
        for(int i = 1; i <= n; i++)
        {
            for(int j = 1; 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点不同的点,两个点是不能算环的。

int mincircle = INF;  
int dis[maxn][maxn];  
int value[maxn][maxn];  
  
void floyd()
{
    memcpy(value,dis,sizeof(value));  
    for(int k = 1; k <= n; k++)/*求最小环,不包含第k个点*/ 
    {  
        for(int i = 1; i < k; i++)/*到k-1即可*/  
        {
            for(int j = i+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有边*/  
        }  
        /*更新最短路*/  
        for(int i = 1 ; i <= n ; i++)
        {  
            for(int j = 1 ; j <= n ; j++)  
                dis[i][j] = min(dis[i][k]+dis[k][j],dis[i][j]);  
        }  
     }    
}  




你可能感兴趣的:(ACM)