zoukankan      html  css  js  c++  java
  • floyd算法

    应用

    • 求多源点最短路
    • 传递闭包
    • 找最小环(对于正权图而言)
    • 恰好经过k条边的最短路

    floyd算法原理

    算法模板

    //初始化:d[i][i] = 0 且不相连的节点距离需要初始化为INF
    for(int k = 0; k < n; k++)
        for(int i = 0; i < n; i++)
            for(int j = 0; j < n; j++)
                d[i][j] = min(d[i][j] , d[i][k] + d[k][j]);
    

    空间优化

    • 需要说明表达式d[k][i][j] = min(d[k][i][j] , d[k-1][i][k] + d[k-1][k][j])d[i][j] = min(d[i][j] , d[i][k] + d[k][j])等价
    • d[k-1][i][k] <=> d[i][k]为例,只需要说明每一次d[i][k]的值可能被更新时,d[i][k]的值不变
    • j=k, 则d[k][i][k] = min(d[k][i][k] , d[k-1][i][k] + d[k-1][k][k]).因为d[k-1][k][k]=0,所以d[i][k]的值不会发生改变,总是第k-1层的值

    例题-牛的旅行

    题目链接

    题解

    1.根据输入建立邻接矩阵d,跑一边floyd算法
    2.求每一个连通块的直径(距离最远的两点的距离),答案必定>=每一连通块的直径
    3.枚举连接两个连通块内的某点所形成的新连通块的直径中的最小值,答案必定>=这个最小值
    4.新的连通块直径(例如连接i,j两点):maxd[i] + dist(i , j) + maxd[j]

    代码

    #include <iostream>
    #include <cstring>
    #include <cmath>
    using namespace std;
    const int N = 160;
    const double INF = 1e20;
    #define x first
    #define y second
    typedef pair<int ,int> PII;
    PII p[N];
    double d[N][N] , maxd[N];
    char g[N][N];
    int n;
    
    double get_dist(PII a , PII b)
    {
        double x = a.x - b.x , y = a.y - b.y;
        return sqrt(x*x + y*y);
    }
    
    int main()
    {
        cin >> n;
        for(int i = 0; i < n; i++)  cin >> p[i].x >> p[i].y;
    
        for(int i = 0; i < n; i++)  cin >> g[i];
    
        //初始化距离
        for(int i = 0; i < n; i++)
            for(int j = 0; j < n; j++)
                if(i != j)
                {
                    if(g[i][j] == '1') d[i][j] = get_dist(p[i] , p[j]);
                    else d[i][j] = INF;
                }
    
        //Floyd算法
        for(int k = 0; k < n; k++)
            for(int i = 0; i < n; i++)
                for(int j = 0; j < n; j++)
                    d[i][j] = min(d[i][j] , d[i][k] + d[k][j]);
    
        //求连通块内的直径
        for(int i = 0; i < n; i++)
            for(int j = 0;j < n; j++)
                if(d[i][j] < INF)
                    maxd[i] = max(maxd[i] , d[i][j]);
    
        //情况1:结果必然 >= 每一连通块的直径
        double ans = 0;
        for(int i = 0; i < n; i++)  ans = max(ans , maxd[i]);
    
        //情况2:结果必然 >= 连接一条边后新的连通块的直径(枚举最小值)
        double res = INF;
        for(int i = 0; i < n; i++)
            for(int j = 0; j < n; j++)
                if(d[i][j] >= INF)
                    res = min(res , maxd[i] + get_dist(p[i] , p[j]) + maxd[j]);
    
        printf("%lf
    " , max(ans , res));
    
        return 0;
    }
    

    例题-排序(传递闭包)

    floyd算法求传递闭包

    • 1.邻接矩阵初始化d[i][j]:1-表示i,j之间存在i到j的单向路径 0-表示不存在路径
    • 2.将转移关系修改为:d[i][j] = d[i][j] | ( d[i][k] & d[k][j] ) -(表示 i->k , k->j两段路径都存在)
    • 3.整体认识:在算法结束后,所有的间接关系,例如 i->j , j->k,会直接表示出来,即d[i][k] = 1

    题解

    • 在本题中可以将A < B类比成A->B
    • 则存在矛盾例如A<B , B<C , C<A , 可以推知A<A , 即A->A , d[i][i] = 1
    • 变量A,B不存在相对关系:d[a][b] = 0 && d[b][a] = 0

    代码

    #include <iostream>
    #include <cstring>
    using namespace std;
    const int N = 30;
    int d[N][N] , g[N][N];
    int n , m;
    
    void floyd()
    {
        memcpy(d , g , sizeof g);
    
        for(int k = 0; k < n; k++)
            for(int i = 0; i < n; i++)
                for(int j = 0; j < n; j ++)
                    d[i][j] |= d[i][k] & d[k][j];
    }
    
    int check()
    {
        for(int i = 0; i < n; i++)
            if(d[i][i]) return 2;
    
        for(int i = 0; i < n; i++)
            for(int j = i + 1; j < n; j++)
                if(!d[i][j] && !d[j][i])  return 0;
    
        return 1;
    }
    
    char get_min(int val)
    {
        for(int i = 0; i < n; i++)
        {
            int cnt = 0;
            for(int j = 0; j < n; j++)
                if(d[i][j]) cnt++;
            if(cnt == val) return i + 'A';
        }
    }
    
    int main()
    {
        while(cin >> n >> m , n || m)
        {
            char str[4];
            //type: 0-表示存在无法判断的关系  1-表示所有关系已确定   2-关系存在矛盾
            int type = 0 , t;
    
            memset(g , 0 , sizeof g);
            for(int i = 1; i <= m; i++)
            {
                cin >> str;
                int a = str[0] - 'A' , b = str[2] - 'A';
    
                if(!type)
                {
                    g[a][b] = 1;
                    floyd();
                    type = check();
                    if(type) t = i;
                }
            }    
                if(!type) printf("Sorted sequence cannot be determined.
    ");
                else if(type == 2)  printf("Inconsistency found after %d relations.
    " , t);
                else
                {
                    printf("Sorted sequence determined after %d relations: " , t);
                    for(int i = 0; i < n; i++)
                        printf("%c" , get_min(n - i - 1));
                    puts(".");
                }
        }
    
        return 0;
    }
    

    参考文献

    Acwing-算法提高课-图论章节
    https://www.acwing.com/activity/content/introduction/16/

  • 相关阅读:
    yzoj P2344 斯卡布罗集市 题解
    yzoj P2350 逃离洞穴 题解
    yzoj P2349 取数 题解
    JXOI 2017 颜色 题解
    NOIP 2009 最优贸易 题解
    CH 4302 Interval GCD 题解
    CH4301 Can you answer on these queries III 题解
    Luogu2533[AHOI2012]信号塔
    Luogu3320[SDOI2015]寻宝游戏
    Luogu3187[HNOI2007]最小矩形覆盖
  • 原文地址:https://www.cnblogs.com/zy200128/p/14173523.html
Copyright © 2011-2022 走看看