Flyod——最短路算法、求最小环

解最短路

复杂度O(n3)
伪码:

for (k = 0; k < n; k++)
   for (i = 0; i < n; i++)
      for (j = 0; j < n; j++)
         if (dis[i, j] > dis[i, k] + dis[k, j])
            dis[i, j] = dis[i, k] + dis[k, j]

接触Floyd有一段时间了,但是对Floyd的印象就是,只记得它可以求任意两点间的最短距离,有三层循环,也不会用。。。。。

今天总结的时候看到挑战程序设计竞赛中的讲解
在这里插入图片描述
看到那个“三维数组”d,当时就懵了。。。。。
在这里插入图片描述

然后翻博客也只明白了,如果要让i、j两个点之间的距离变短,可以引入一个中转点k:i—>k—>j,或者多个中转点k1、k2:i—>k1—>k2—>j

直到后面看见啊哈里面的一句话:
在这里插入图片描述
对,因为我们存的是邻接矩阵,所如果只能通过一个顶点来中转的话,值需要遍历一下dis数组(dis[i][j]表示顶点i到j的距离,并且d[i][j]的值在循环的过程中不断优化),判断是否可以通过1号点中转来获得更短的路径,执行一下dis[i][j] = min(dis[i][j], dis[i][1] + dis[1][j])

然后的操作(有点像二维数组的01背包,都是在前面的基础上进行操作,当枚举顶点k之前我们已经求得了只有顶点1~k-1作为中转点时的最短路)
在这里插入图片描述
在这里插入图片描述
直到最后能通过所有点中转就能得到任意两点之间的最短距离。

然后画了个图理解下算法最终是怎么得到最短路径的:
在这里插入图片描述
(只看2到3的最短距离,最开始边权都存在dis数组里面,dis[2][3]=103)
k=5时,可以通过5更新dis,dis[2][3]=23+34;k=7时,可以通过7更新dis,就是7到2的dis值+7到3的dis值=3+(12+34),然后我们会发现点7加进来时,就有:2到7的最短路径+7到3的最短路径=2到3的最短路径,而后面这两个最短路径已经在点7加入之前更新完成了;如果还有第8个点可以更新dis[2][3],那么肯定也有:2到8的最短路径+8到3的最短路径=2到3的最短路径,所以这样遍历下去的话我们就可以得到最终的最短路径。

求最短路的代码:Floyd算法(多源最短路)

#include<bits/stdc++.h>
 
using namespace std;
const int maxn = 105;
const int INF = 0x3f3f3f3f;
int dis[maxn][maxn];
///dis[i][j]表示 i到j的最短距离
void init() {
    for (int i = 1; i < maxn; i++) {
        for (int j = 1; j < maxn; j++)
            dis[i][j] = INF;
        dis[i][i] = 0;
    }
} 
int main() {
    init();
    int n, m, s, e, c;
    scanf("%d %d", &n, &m);
    while (m--) {
        scanf("%d %d %d", &s, &e, &c);
        dis[s][e] = min(dis[s][e], c);//可能存在重边
        // 无向边  dis[e][s]=min(dis[e][s], c);
    }
    for (int k = 1; k <= n; k++)
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++)
                dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
                //这个过程中,dis[i][j]的值都是i到j的路径不断优化的结果
    return 0;
}

最小环(无向边)

参考了dalao的博客:Floyd最小环算法

问题描述: 给你一张无向图,定义环为从i出发到达j然后从j返回i并且所有点都只经过一次(最少为3个点),求所有环当中经过路径最小的环

算法描述:
首先容易想到的是暴力来枚举环,当删除其中一条边ij后再跑一边从i到j的最短路,然后加上边ij的值就是含有边ij的最小环的值,这样最坏的时间复杂度可以达到O(n4),显然复杂度有点大。(再准确点来说复杂度是O(e*n2),e是图中不重复边的条数)

所以我们考虑降低时间复杂度,因为要保证最少三个点,所以我们可以枚举任意三个不同的点,然后看其中两个点有0个、1个、2个、3个、、、中转点时可能会更新的最短路径长度。

我们来分析下floyd的实现过程,当枚举顶点k之前我们已经求得了顶点为1 - k-1 的最短路,所以我们可以在跟新k之前枚举k之前的i和j的组合

我们可以知道dis[i][j]没有经过k点,所以我们就可以知道如果dis[i][j]+mp[i][k]+mp[k][j] != inf(mp[i][j]为没有跟新得边值) 时就存在一条经过ijk的最小环,所以我们要求的是所有环当中最小的哪一个!

HDU1599:

#include<bits/stdc++.h>  //以hdu1599为例
using namespace std;
#define INF 1e8
int e[105][105];
int dis[105][105];
int main()
{
    int n,m,s,e,len,dis[200][200],mp[200][200];
    while(cin>>n>>m)
    {
        int mmin=INF;
        for(int i=1; i<=n; i++)
            for(int j=1; j<=n; j++)
                if(i==j)
                    dis[i][j]=mp[i][j]=0;
                else
                    dis[i][j]=mp[i][j]=INF;
        while(m--)
        {
            scanf("%d%d%d",&s,&e,&len);
            dis[s][e]=mp[s][e]=dis[e][s]=mp[e][s]=min(len,mp[s][e]);
        }
        for(int k=1; k<=n; k++)
        {
            for(int i=1; i<k; i++)
                for(int j=1; j<k; j++)//有的代码初始化的=i+1,主要就是为了保证i!=j
                    if(i!=j)
                        mmin=min(mmin,dis[i][j]+mp[i][k]+mp[k][j]);
            for(int i=1; i<=n; i++)
                for(int j=1; j<=n; j++)
                    dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
        }
        if(mmin==INF)
            printf("It's impossible.\n");
        else
            printf("%d\n",mmin);
    }
    return 0;
}

最小环(有向边)

上面的代码也可以解决有向边的情况,只是还需要改一下里面边得起始点,或者直接跑一边Floyd最短路,然后遍历一遍dis[i][j]+mp[j][i]求最小环

有向边:关于Floyd求解最小环的问题

发布了52 篇原创文章 · 获赞 26 · 访问量 3173

猜你喜欢

转载自blog.csdn.net/qq_43803508/article/details/100085289