• 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/

  • 相关阅读:
    c中的数组与字符串
    c中的函数
    C中的流程控制
    c中的基本运算
    scanf函数
    c中的数据类型、常量、变量
    c中的关键字、标识符、注释
    ios必须知道的事情
    安卓开发之获取SD卡空间数据
    安卓日志猫的使用
  • 原文地址:https://www.cnblogs.com/zy200128/p/14173523.html
Copyright © 2020-2023  润新知