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)
上一篇 2022年6月16日 下午10:07
下一篇 2022年6月16日 下午10:07


相关推荐

  • 安装Mssql2000时,提示以前的某个程序安装挂起的解决方法

    安装Mssql2000时,提示以前的某个程序安装挂起的解决方法

    2021年8月9日
    62
  • 2026年3月18日
    2
  • 硬件知识汇总

    硬件知识汇总硬件知识1、电源类1.1 电源基础各种“地”——各种“GND”板载电源设计规范电源环路稳定性评价方法深入芯片内部,理解去耦电容的作用减小DC/DC变换器中的接地反弹——一些接地要点开关电源中的小启示电源相关的测试去耦电容的选择、容值计算和布局布线可充电电池将被超级电容取代电容去耦原理(解释十分透彻)地线要短——测试开关电源纹波时权衡电源与PCB设计极点是男人,零点是女人开关电源仿真(sab…

    2022年7月22日
    16
  • taglib实例

    taglib实例taglib 的实例 1Java 文件 nbsp nbsp OutputTag java nbsp nbsp nbsp Created nbsp on nbsp 2008 年 4 月 13 日 nbsp 下午 10 08 nbsp nbsp nbsp To nbsp change nbsp this nbsp template nbsp choose nbsp Tools nbsp nbsp Template nbsp Manager nbsp nbsp and nbsp open nbsp the nbsp template nbsp in nbsp the nbsp editor nbsp package nbsp com impor

    2026年3月16日
    1
  • 人工智能进步如何驱动编程学习的新潮流?

    人工智能进步如何驱动编程学习的新潮流?

    2026年3月12日
    2
  • vue中的render函数

    vue中的render函数render 函数跟 template 一样都是创建 html 模板的 但是有些场景中用 template 实现起来代码冗长繁琐而且有大量重复 这时候就可以用 render 函数

    2026年3月17日
    1

发表回复

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

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