floyed详解[通俗易懂]

floyed详解[通俗易懂]显然Floyed算法是一个简短而好理解的算法,这里指的好理解是因为Floyed的代码长度不大,实在没理解都可以背下来,所以说是好理解,实际上是真的好理解吗?我们来看看最基础的FloyedFloyed是什么?自然是用来求多源最短路的啦,时间效率是O(n^3),有人会问那我不对每个点做一遍SPFA或dijkstra堆优化,时间效率是O(n^2logn)那不是快很多?实际上因为Floyed

大家好,又见面了,我是你们的朋友全栈君。

显然Floyed算法是一个简短而好理解的算法,这里指的好理解是
因为Floyed的代码长度不大,实在没理解都可以背下来,所以说是好理解,实际上是真的好理解吗?我们来看看

最基础的Floyed

Floyed是什么?自然是用来求多源最短路的啦,时间效率是O(n^3),有人会问那我不对每个点做一遍SPFA或dijkstra堆优化,时间效率是O(n^2logn)那不是快很多?实际上因为Floyed常数很小,所以真正比起来,时间效率差不多,所以你是愿意只写5行代码,还是冗杂地打这么长的代码???

Floyed其实用到的是DP的思想f[k,i,j] = min(f[k-1,i,k]+f[k-1,k,j] , f[k-1,i,j]),k是我们用来枚举的断点,说是断点其实就是前K个,只对后面我们将最小环还有其他应用有很大的帮助,因为k是不断递增的,我们可以利用这个性质来做许多题目。通过枚举k,我们可以很快得出任意一点x->y的最短路径。
下面贴上代码

    for(k=1;k<=n;k++)  
      for(i=1;i<=n;i++)  
        for(j=1;j<=n;j++)  
          if(a[i][j]>a[i][k]+a[k][j])  
            a[i][j]=a[i][k]+a[k][j];  

是不是简单而好理解???

传递闭包

这是个啥东西?如果我们假设a可以到b,b可以到c,那么a是不是可以到c???实际上这个东西就是指图的连通性,用Floyed处理是不是快很多???比较dfs的话,是优化了很多了。
下面贴上代码

    REP(k,1,n)
    {
        REP(i,1,n)
        {
            REP(j,1,n)
            {
                a[i][j]|=(a[i][k]&a[k][j]);
            }
        }
    }

可以做一下POJ3660Cow Contest是一道裸题
注:&是两个二进制位上有0,就是0,|操作是有1,就是1.

最小环

这个东西怎么说呢???其实我们可以发现如果图里面存在环的话,我们假设i,j间有一条简单路径,如果有环我们可以通过另一个点k到达,是不是跟Floyd算法很相似???所以我们最外层的这个循环,我们用来枚举前k个点,找前k个点中的最小环,后面的Floyd照常进行就可以了,下面的代码还有记录路径,pre[i][j],表示从i到j离j最近的一个点。
POJ1734

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cstdlib>
#include<cmath>
#include<algorithm>

using namespace std;

#define REP(i,a,b) for(register int i = (a), i##_end_ = (b); i <= i##_end_; ++i)

inline int read()
{
    char c = getchar();register int fg = 1, sum = 0;
    while(c < '0' || c > '9')
    {
        if(c == '-')fg = -1;
        c = getchar();
    }
    while(c >= '0' && c <= '9')
    {
        sum = sum * 10 + c - '0';
        c = getchar();
    }
    return fg * sum;
} 

int n, m;
int a[200][200],dis[200][200],pre[200][200],path[100000];
#define inf 0x7ffffff

int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        memset(a,0,sizeof(a));
        memset(pre,0,sizeof(pre));
        memset(dis,0,sizeof(dis));
        memset(path,0,sizeof(path));
        REP(i,1,n)REP(j,1,n)a[i][j] = inf,pre[i][j] = i;
        REP(i,1,m)
        {
            int x,y,z;
            x = read(), y = read(), z = read();
            a[x][y] = a[y][x] = min(a[y][x],z);
        }
        memcpy(dis,a,sizeof(dis));
        int ans = inf,num = 0;
        REP(k,1,n)
        {
            for(int i = 1; i < k; i++)
            {
                for(int j = i+1; j < k; j++)
                {
                    int tmp = dis[i][j] + a[i][k] + a[k][j];
                    if(tmp < ans)
                    {
                        ans = tmp;
                        num = 0;
                        int now = j;
                        while(now != i)
                        {
                            path[num++] = now;
                            now = pre[i][now];
                        }
                        path[num++] = i,path[num++] = k;
                    }
                }
            }
            REP(i,1,n)
            {
                REP(j,1,n)
                {
                    if(dis[i][j] > dis[i][k]+dis[k][j])
                    {
                        dis[i][j] = dis[i][k] + dis[k][j];
                        pre[i][j] = pre[k][j];
                    }
                }
            }
        }
        if(ans == inf){printf("No solution.\n");continue;}
        int i;
        for(i = 0; i < num-1; i++)printf("%d ",path[i]);
        printf("%d\n",path[i]);
    }
    return 0;
}

其他用途

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cstdlib>
#include<cmath>
#include<algorithm>

using namespace std;

#define REP(i,a,b) for(register int i = (a), i##_end_ = (b); i <= i##_end_; ++i)

inline int read()
{
    char c = getchar();register int fg = 1, sum = 0;
    while(c < '0' || c > '9')
    {
        if(c == '-')fg = -1;
        c = getchar();
    }
    while(c >= '0' && c <= '9')
    {
        sum = sum * 10 + c - '0';
        c = getchar();
    }
    return fg * sum;
} 

int n, m;
int a[200][200],dis[200][200],pre[200][200],path[100000];
#define inf 0x7ffffff

int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        memset(a,0,sizeof(a));
        memset(pre,0,sizeof(pre));
        memset(dis,0,sizeof(dis));
        memset(path,0,sizeof(path));
        REP(i,1,n)REP(j,1,n)a[i][j] = inf,pre[i][j] = i;
        REP(i,1,m)
        {
            int x,y,z;
            x = read(), y = read(), z = read();
            a[x][y] = a[y][x] = min(a[y][x],z);
        }
        memcpy(dis,a,sizeof(dis));
        int ans = inf,num = 0;
        REP(k,1,n)
        {
            for(int i = 1; i < k; i++)
            {
                for(int j = i+1; j < k; j++)
                {
                    int tmp = dis[i][j] + a[i][k] + a[k][j];
                    if(tmp < ans)
                    {
                        ans = tmp;
                        num = 0;
                        int now = j;
                        while(now != i)
                        {
                            path[num++] = now;
                            now = pre[i][now];
                        }
                        path[num++] = i,path[num++] = k;
                    }
                }
            }
            REP(i,1,n)
            {
                REP(j,1,n)
                {
                    if(dis[i][j] > dis[i][k]+dis[k][j])
                    {
                        dis[i][j] = dis[i][k] + dis[k][j];
                        pre[i][j] = pre[k][j];
                    }
                }
            }
        }
        if(ans == inf){printf("No solution.\n");continue;}
        int i;
        for(i = 0; i < num-1; i++)printf("%d ",path[i]);
        printf("%d\n",path[i]);
    }
    return 0;
}

FLoyd算法用来解决其他的一些多源最短路的问题有想不到的妙处,比如我们所说的洛谷1119灾后重建为例:

B地区在地震过后,所有村庄都造成了一定的损毁,而这场地震却没对公路造成什么影响。但是在村庄重建好之前,所有与未重建完成的村庄的公路均无法通车。换句话说,只有连接着两个重建完成的村庄的公路才能通车,只能到达重建完成的村庄。
题目描述

给出B地区的村庄数N,村庄编号从0到N-1,和所有M条公路的长度,公路是双向的。并给出第i个村庄重建完成的时间t[i],你可以认为是同时开始重建并在第t[i]天重建完成,并且在当天即可通车。若t[i]为0则说明地震未对此地区造成损坏,一开始就可以通车。之后有Q个询问(x, y, t),对于每个询问你要回答在第t天,从村庄x到村庄y的最短路径长度为多少。如果无法找到从x村庄到y村庄的路径,经过若干个已重建完成的村庄,或者村庄x或村庄y在第t天仍未重建完成 ,则需要返回-1。
输入输出格式
输入格式:

输入文件rebuild.in的第一行包含两个正整数N,M,表示了村庄的数目与公路的数量。

第二行包含N个非负整数t[0], t[1], …, t[N – 1],表示了每个村庄重建完成的时间,数据保证了t[0] ≤ t[1] ≤ … ≤ t[N – 1]。

接下来M行,每行3个非负整数i, j, w,w为不超过10000的正整数,表示了有一条连接村庄i与村庄j的道路,长度为w,保证i≠j,且对于任意一对村庄只会存在一条道路。

接下来一行也就是M+3行包含一个正整数Q,表示Q个询问。

接下来Q行,每行3个非负整数x, y, t,询问在第t天,从村庄x到村庄y的最短路径长度为多少,数据保证了t是不下降的。

输出格式:

输出文件rebuild.out包含Q行,对每一个询问(x, y, t)输出对应的答案,即在第t天,从村庄x到村庄y的最短路径长度为多少。如果在第t天无法找到从x村庄到y村庄的路径,经过若干个已重建完成的村庄,或者村庄x或村庄y在第t天仍未修复完成,则输出-1。

输入输出样例
输入样例#1:

4 5
1 2 3 4
0 2 1
2 3 1
3 1 2
2 1 4
0 3 5
4
2 0 2
0 1 2
0 1 3
0 1 4

输出样例#1:

-1
-1
5
4

说明

对于30%的数据,有N≤50;

对于30%的数据,有t[i] = 0,其中有20%的数据有t[i] = 0且N>50;

对于50%的数据,有Q≤100;

对于100%的数据,有N≤200,M≤N*(N-1)/2,Q≤50000,所有输入数据涉及整数均不超过100000。

看数据范围我们很容易想到Floyed,但是怎么做呢???我们不妨假设我们所枚举的k点是指的前k个村庒,由于出题者良心地帮我们排好了顺序,于是我们在Floyd的时候枚举询问,由于你枚举到这个点k,经过k点之前的最短路是被更新出来了的,是不是很明了???

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>

using namespace std;

#define REP(i,a,b) for(register int i = (a), i##_end_ = (b); i <= i##_end_; ++i)

inline int read()
{
    char c = getchar();register int fg = 1, sum = 0;
    while(c < '0' || c > '9')
    {
        if(c == '-')fg = -1;
        c = getchar();
    }
    while(c >= '0' && c <= '9')
    {
        sum = sum * 10 + c - '0';
        c = getchar();
    }
    return fg * sum;
}

const int maxn = 100000+10;
int n,m,t[500],qx[maxn],qy[maxn],qt[maxn],dis[500][500];

int main()
{
    n = read(), m = read();
    memset(dis,0x3f,sizeof(dis));
    REP(i,0,n-1)t[i] = read();
    REP(i,1,m)
    {
        int x,y,z;
        x = read(), y = read(), z = read();
        dis[x][y] = z,dis[y][x] = z;
    }
    int q = read();
    REP(i,1,q)
    {
        qx[i] = read(), qy[i] = read(), qt[i] = read();
    }
    int now = 1;
    t[n] = t[n-1] + 1;
    REP(k,0,n-1)
    {
        while(now <= q && t[k] > qt[now])
        {
            int ans = dis[qx[now]][qy[now]];
            if(t[qx[now]] >= t[k] || t[qy[now]] >= t[k])ans = -1;
            printf("%d\n",ans == dis[n][n] ? -1 : ans);
            now++;
        }
        REP(i,0,n-1)
        {
            REP(j,0,n-1)
            {
                if(dis[i][j] > dis[i][k] + dis[k][j])
                    dis[i][j] = dis[i][k] + dis[k][j];
            }
        }
    }
    while(now <= q)
    {
        int ans = dis[qx[now]][qy[now]];
        printf("%d\n",ans == dis[n][n] ? -1 : ans);
        now++;
    }
}

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/149445.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • mybatis开发dao两种方法

    mybatis开发dao两种方法mybatis是一个支持普通SQL查询,存储过程和高级映射的优秀的持久层的框架,是apache下的顶级项目。mybatis消除了几乎所有的JDBC代码和参数的手工设置以及对结果集的检索封装。mybatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO映射成数据库中的记录。其中,开发dao有两种方法,一种原始的dao开发方法,程序员需要写dao接口和dao实现类。另一种…

    2022年6月13日
    34
  • centos7 安装gitea使用

    centos7 安装gitea使用参考官网 https gitea iohttps docs gitea iohttps docs gitea io en us install from binary 关于 GiteaGitea 是一个自己托管的 Git 服务程序 他和 GitHub BitbucketorG 等比较类似 他是从 Gogs 发展而来 不过我们已经 Fork 并且命名为 Gitea 对

    2025年9月25日
    4
  • 微软modern.IE网站,多版本IE免费测试工具集建议收藏

    微软今天发布了modern.IE,这是一系列免费的、针对Web开发者的测试工具和资源集合网站,微软希望以此来帮助开发者更轻松地实现跨IE和其他现代浏览器、跨设备的兼容性,其他还有代码检测工具、标

    2021年12月21日
    44
  • Arcgis地图切片以及发布

    Arcgis地图切片以及发布Arcgis 地图切片以及发布过程简介地图切片地图切片主要是为了提高地图的浏览速度 可以在地图发布之前首先进行切片 也可以在地图发布时直接利用缓存切片 后者在发布大型地图时不建议采用 速度很慢 1 1 利用工具 arcgis gt Datamanageme gt Tilecache gt Generatetile 生成 xm

    2025年8月13日
    4
  • maven配置阿里云仓库地址

    maven配置阿里云仓库地址<mirror><id>nexus-aliyun</id><mirrorOf>central</mirrorOf><name>Nexusaliyun</name><url>http://maven.aliyun.com/nexus/content/groups/public</url></mirror>

    2022年6月25日
    26
  • 007 矩阵的秩定义、秩求法、秩的性质「建议收藏」

    007 矩阵的秩定义、秩求法、秩的性质「建议收藏」007矩阵的秩定义、秩求法、秩的性质

    2022年5月15日
    35

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号