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++;
    }
}

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

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

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • Jlink或者stlink用于SWD接口下载程序

    Jlink或者stlink用于SWD接口下载程序最近要使用stm32f103c8t6最小系统板,直接ISP串口下载程序太麻烦,就想着使用swd接口来调试。结果:通过SWD接口下载程序成功,但调试失败,还不知原因,会的的人麻烦交流一下。SWD接口:3.3VDIO(数据)CLK(时钟)GND1.首先声明jlink和stlink都有jtag和swd调试功能。jlink接口如下:如图,我使用的就是VCC…

  • 聊聊eureka的delta配置

    聊聊eureka的delta配置

  • python数据分析源码_python 统计分析

    python数据分析源码_python 统计分析以后都在github更新,请参考CpythonInternals版本第一步克隆Cpython仓库到本地,切换到我当前的版本,我当前的版本号是3.8.0a0gitclonehttps://github.com/python/cpython.gitgitreset–hardab54b9a130c88f708077c2ef6c4963b632c132b…

  • ideal的debug_idea debug怎么用

    ideal的debug_idea debug怎么用Debug介绍Debug设置如上图标注1所示,表示设置Debug连接方式,默认是Socket。Sharedmemory是Windows特有的一个属性,一般在Windows系统下建议使用此设置,相对于Socket会快点。Debug常用快捷键快捷键 介绍 F7 在Debug模式下,进入下一步,如果当前行断点是一个方法,则进入当…

  • Sql学习笔记-declare用法

    Sql学习笔记-declare用法栗子一:IF 1=1BEGIN    DECLARE @test VARCHAR    SET @test=’1′       PRINT ‘in if:’+@testEND运行看结果输出inif:1这是可以预想的结果。那我们在if外面使用变量@test试试。栗子二:IF 1=1BEGIN   DECLARE @test VARCHAR   SET @test=’…

  • 验证码Kaptcha的使用「建议收藏」

    验证码Kaptcha的使用「建议收藏」Kaptcha是一个非常实用的验证码生成工具,可以通过配置生成多样化的验证码。以图片的形式显示,从而无法进行复制粘贴。

发表回复

您的电子邮箱地址不会被公开。

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