图论(4)Floyd算法

news2024/11/26 9:58:01

一、概述

floyd算法主要作用有:1.找最短路   2.求传递闭包   3.找最小环   4.求出恰好经过k条边的最短路

本文章将介绍floyd求最短路的证明以及以上四个作用的实践。

二、floyd算法求最短路的证明

之前就多次提到过图论与dp问题的联系,floyd算法可以由dp思想来推导

状态表示:d[i,j,k],表示从i点到j点,中间(不包含两头)经过的节点编号不超过k的路径中最短的路径长度。

状态集合:从i点到j点,中间经过节点编号不超过k的所有路径

属性:最短长度

状态计算集合划分:所有不含k号点的路径,所有包含k号点的路径。划分依据是路径选不选k号点

状态转移方程:如果不选k号点,则结果仍为d(k-1,i,j)。如果选择k号点,即在路径中加入k号点。那么怎么样加是能求出最短的那一条呢,答案为从i到k的最短路加上从k到j的最短路。所以状态转移方程为:

d[k,i,j]=min(d[k-1,i,j],d[k-1,i,k]+d[k-1,k,j])

根据我们在优化背包问题时积累的知识,我们知道实际上这个数组在计算第k层的时候只用到了k-1层的信息,即可省略k这个维度。dp顺序为:枚举k,枚举i,枚举j,再状态转移。和我们之前提过的floyd算法求最短路一模一样。

三、例题

1.acw1125牛的旅行

第一眼没我没啥思路,因为n比较小,所以考虑纯纯的暴力做法。

首先思考一下答案组成:如果不连线,最小直径可能是某个连通块里最大的最短路。

如果连线,就直接暴力枚举所有可能连接点i,j。会新生成一条“可能的答案”,即从i点出发到原连通块的最长最短路+i到j+j到原连通块的最长最短路。

floyd算法求最短路,暴力枚举所有点,求出从i点出发到其连通块内的最长的最短路maxd数组。再求maxd的最大值res1。

然后暴力枚举连接i,j。求出产生的最短的“可能答案”res2。

res1为不连边时的直径,如果res1大于res2,则连边后直径不变,答案为res1

如果res1大于res2,则直径变为res2,答案为res2。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#define x first
#define y second
using  namespace std;

const double INF =1e20;
const int N =150;
typedef pair<int,int> PII;

//n很小,可以直接暴力处理出距离第i个点最远的点的距离。
//直径有两种情况 1.不连边,直接就是某个连通块里的直径  
//2.暴力遍历连i,j  连边后直径可能是i,j距离+原连通块内离i最远的距离+原连通块内里j最远的距离。
//最终答案是两种情况中的最大值。

PII q[N];
int n;
double d[N][N],maxd[N];
char g[N][N];


double getdist(PII a,PII b)
{
    double dx=a.x-b.x,dy=a.y-b.y;
    return sqrt(dx*dx+dy*dy);

}

int main()
{
    cin>>n;
    for(int i=0;i<n;i++) cin>>q[i].x>>q[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]=getdist(q[i],q[j]);
                else d[i][j]=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]);
            }

    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]);

    double res1=0;
    for(int i=0;i<n;i++) res1=max(res1,maxd[i]);


    double res2=INF;
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
            if(d[i][j]==INF)
            {
                res2=min(res2,getdist(q[i],q[j])+maxd[i]+maxd[j]);
            }
    printf("%lf",max(res1,res2));

}

作者:yankai
链接:https://www.acwing.com/activity/content/code/content/4385046/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

2.求传递闭包 acwing343排序

传递闭包 O(mn3) 

 题意简述:给出若干关系,求出当前信息可求出的所有关系。。其实就是求传递闭包

根据离散数学知识,对i,j更新n次即为传递闭包,多的不说直接看代码。

如何判断出现矛盾:如果发现:从 x 能到达 y 并且从 y 也能到达 x(即,x 比 y 成绩好并且 y 比 x 成绩好),那就是出现矛盾了。即存在关系(x,x);

题目要求求出从小到大的排序,那我们设置一个求最小值函数,如果i和每个数都有关系,则i是最小值,把i弹出。继续求最小值,求n次即得排序。

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;

const int N =26;

int n,m;
bool g[N][N],d[N][N];
bool st[N];


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=0;j<n;j++)
            if(i!=j&&!d[i][j]&&!d[j][i])//存在未确定关系
                return 0;
    return 1;
}

char getmin()
{
    for(int i=0;i<n;i++)
        if(!st[i])
        {
            bool flag=true;
            for(int j=0;j<n;j++)
                if(!st[j]&&d[j][i]) //j还未输出 且j<i
                {
                    flag=false;
                    break;
                }
            if(flag)
            {
                st[i]=true;
                return i+'A';
            }
        }

}

int main()
{
    while(cin>>n>>m,n||m)
    {
        memset(g,false,sizeof g);
        int type=0,t;
        for(int i=1;i<=m;i++)
        {
            char str[5];
            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) puts("Sorted sequence cannot be determined.");
        else if(type==2) printf("Inconsistency found after %d relations.\n", t);
        else
        {
            memset(st,0,sizeof st);
            printf("Sorted sequence determined after %d relations: ", t);
            for(int i=0;i<n;i++)
                cout<<getmin();
            cout<<"."<<endl;
        }
    }
    return 0;
}

作者:yankai
链接:https://www.acwing.com/activity/content/code/content/4385357/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

增量算法 O(mn2)

#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 26;

int n, m;
bool d[N][N];
bool st[N];

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 = 0; j < i; j ++ )
            if (!d[i][j] && !d[j][i])
                return 0;

    return 1;
}

char get_min()
{
    for (int i = 0; i < n; i ++ )
        if (!st[i])
        {
            bool flag = true;
            for (int j = 0; j < n; j ++ )
                if (!st[j] && d[j][i])
                {
                    flag = false;
                    break;
                }
            if (flag)
            {
                st[i] = true;
                return 'A' + i;
            }
        }
}

int main()
{
    while (cin >> n >> m, n || m)
    {
        memset(d, 0, sizeof d);

        int type = 0, t;
        for (int i = 1; i <= m; i ++ )
        {
            char str[5];
            cin >> str;
            int a = str[0] - 'A', b = str[2] - 'A';

            if (!type)
            {
                d[a][b] = 1;
                for (int x = 0; x < n; x ++ )
                {
                    if (d[x][a]) d[x][b] = 1;
                    if (d[b][x]) d[a][x] = 1;
                    for (int y = 0; y < n; y ++ )
                        if (d[x][a] && d[b][y])
                            d[x][y] = 1;
                }
                type = check();
                if (type) t = i;
            }
        }

        if (!type) puts("Sorted sequence cannot be determined.");
        else if (type == 2) printf("Inconsistency found after %d relations.\n", t);
        else
        {
            memset(st, 0, sizeof st);
            printf("Sorted sequence determined after %d relations: ", t);
            for (int i = 0; i < n; i ++ ) printf("%c", get_min());
            printf(".\n");
        }
    }

    return 0;
}

作者:yxc
链接:https://www.acwing.com/activity/content/code/content/145995/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 3.acwing344观光之旅(求无向图最小环)

 dp思路:和floyd算法类似,按照环上编号最大的点分类。

求每一类的最小值:floyd第k层时,floyd矩阵已知所有点,从i到j只经过1到k-1点的最短路径。

则在此时可求环最小值。

 记录方案:floyd算法求更新的点k

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 110, INF = 0x3f3f3f3f;

int n, m;
int d[N][N], g[N][N];  // d[i][j] 是不经过点
int pos[N][N];  // pos存的是中间点k
int path[N], cnt;  // path 当前最小环的方案, cnt环里面的点的数量

// 递归处理环上节点
void get_path(int i, int j) {
    if (pos[i][j] == 0) return;  // i到j的最短路没有经过其他节点

    int k = pos[i][j];  // 否则,i ~ k ~ j的话,递归处理 i ~ k的部分和k ~ j的部分
    get_path(i, k);
    path[cnt ++] = k;  // k点放进去
    get_path(k, j);
}

int main() {
    cin >> n >> m;

    memset(g, 0x3f, sizeof g);
    for (int i = 1; i <= n; i ++) g[i][i] = 0;

    while (m --) {
        int a, b, c;
        cin >> a >> b >> c;
        g[a][b] = g[b][a] = min(g[a][b], c);
    }

    int res = INF;
    memcpy(d, g, sizeof g);
    // dp思路, 假设k是环上的最大点, i ~ k ~ j(Floyd的思想)
    for (int k = 1; k <= n; k ++) {

        // 求最小环, 
        //至少包含三个点的环所经过的点的最大编号是k
        for (int i = 1; i < k; i ++)  // 至少包含三个点,i,j,k不重合
            for (int j = i + 1; j < k; j ++)  
            // 由于是无向图,
            // ij调换其实是跟翻转图一样的道理
            // 直接剪枝, j从i + 1开始就好了
            // 更新最小环, 记录一下路径
                if ((long long)d[i][j] + g[j][k] + g[k][i] < res) {
                    // 注意,每当迭代到这的时候, 
                    // d[i][j]存的是上一轮迭代Floyd得出的结果
                    // d[i][j] : i ~ j 中间经过不超过k - 1的最短距离(k是不在路径上的)
                    res = d[i][j] + g[j][k] + g[k][i];  
                    cnt = 0;
                    path[cnt ++] = k;  // 先把k放进去
                    path[cnt ++] = i;  // 从k走到i(k固定的)
                    get_path(i ,j);  // 递归求i到j的路径
                    path[cnt ++] = j;  // j到k, k固定
                }

        // Floyd, 更新一下所有ij经过k的最短路径
        for (int i = 1; i <= n; i ++) 
            for (int j = 1; j <= n; j ++)   
                if (d[i][j] > d[i][k] + d[k][j]) {
                    d[i][j] = d[i][k] + d[k][j];  
                    pos[i][j] = k;
            }
    }

    if (res == INF) puts("No solution.");
    else {
        for (int i = 0; i < cnt; i ++) cout << path[i] << ' ';
        cout << endl;
    }

    return 0;
}

作者:Sean今天AC了吗
链接:https://www.acwing.com/solution/content/20140/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

4.floyd算法的边数特性(倍增算法)

设邻接矩阵A,求A的n次幂,易发现a[i][j]为从i到j长度为n条边的路的条数。

类比:设带权矩阵A,求A*A,a[i][j]=min(a[i][k]+a[k][j]) 则为经过了n条边的最短距离。

 本题就是类似做法。

首先tmax=100,所以最多用200个点,先离散化以免超时。

然后快速幂算法求倍增。O(n^3*logN)

#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
using namespace std;

const int N =210;

int k,n,m,S,E;
int g[N][N];
int res[N][N];

void mul(int c[][N],int a[][N],int b[][N])
{
    static int temp[N][N];
    memset(temp,0x3f,sizeof temp);
    for(int k=1;k<=n;k++)
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                temp[i][j]=min(temp[i][j],a[i][k]+b[k][j]);

    memcpy(c,temp,sizeof temp);
}

void qmi()
{
    memset(res,0x3f,sizeof res);
    for(int i=1;i<=n;i++) res[i][i]=0;

    while(k)
    {
        if(k&1) mul(res,res,g);
        mul(g,g,g);
        k>>=1;
    }
}

int main()
{
    cin>>k>>m>>S>>E;
    memset(g,0x3f,sizeof g);//经过一条边从i到j的最短路  因此i~i是INF
    map<int,int> ids;

    if(!ids.count(S)) ids[S]=++n;
    if(!ids.count(E)) ids[E]=++n;
    S=ids[S],E=ids[E];
    while(m--)
    {
        int a,b,c;
        cin>>c>>a>>b;
        if(!ids.count(a)) ids[a]=++n;
        if(!ids.count(b)) ids[b]=++n;
        a=ids[a],b=ids[b];
        g[a][b]=g[b][a]=min(c,g[a][b]);
    }

    qmi();
    cout<<res[S][E]<<endl;
    return 0;

}

作者:yankai
链接:https://www.acwing.com/activity/content/code/content/4395120/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/181045.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Go依赖管理

"做讨厌潮汐的稚童&#xff0c;祈祷月球失踪。"一、背景我们写一个程序&#xff0c;例如输出hello world 或者 一个猜数字游戏&#xff0c;这些用到的单体函数接口&#xff0c;只需要依赖一些原生的SDK即可。但是&#xff0c;面对复杂的实际问题、工程&#xff0c;仅…

使用构建工具创建Vue项目

使用构建工具创建Vue项目一、使用vue-cli脚手架构建vue项目创建步骤&#xff1a;二、使用 Vite构建vue项目创建步骤&#xff1a;一、使用vue-cli脚手架构建vue项目 Vue CLI 是一个基于 Vue.js 进行快速开发的完整系统&#xff0c;提供&#xff1a; 1 通过 vue/cli 实现的交互式…

演讲比赛流程管理系统

1. 演讲比赛程序需求 1.2程序功能 2. 项目创建 创建名为speech_contest的目录名称 3. 创建管理类 功能描述&#xff1a; 提供菜单界面与用户交互 对演讲比赛流程进行控制 与文件的读写交互 3.1 创建文件 在头文件和源文件的文件夹下分别创建speech…

ubuntu 学习笔记

环境&#xff1a;Ubuntu 22.04 桌面版和server版 一、更换国内源&#xff0c;下载更快 1、源文件路径&#xff1a;/etc/apt/sources.list&#xff0c;到这个路径下备份一下源文件。 #备份原有配置文件命令 sudo cp -r /etc/apt/sources.list /etc/apt/sources.list.backup …

C primer plus学习笔记 —— 14、限定关键字(const、volatile、restrict、_Atomic)

文章目录const 关键字修饰变量修饰指针修饰形参修饰全局变量volatile关键字restrict关键字_Atomic关键字&#xff08;c11&#xff09;const 关键字 修饰变量 将变量变为只读 const int nochange; nochange 4; //不允许 const int a 5; //没问题const int a[3] {3, 5, 6};…

Hive--14---使用sum() over() 实现累积求和

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录Hive中使用over()实现累积求和1.总求和sum(需要求和的列) over(partition by 分组列 )数据准备需求1以地区号网点号币种 为唯一键&#xff0c;求总的金额需求2以地区…

python图像处理(高斯滤波)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】 在谈高斯滤波之前,我们不妨回顾一下之前谈到的均值滤波和中值滤波。均值滤波,就是对像素点以及周围的8个点计算平均值,然后赋值给新像素点。而中值滤波,则是对像素点及周围的8个…

6. 初识多线程编程

1. 多线程 多线程非常重要&#xff0c;工作中用到的也是非常多&#xff0c;面试时也100%会问多线程。 关于多线程的相关知识&#xff0c;可以参考《计算机操作系统(第四版)》&#xff0c;或者自行百度查看有关文章以及视频都可以&#xff0c;此处不再赘述。 2. python中的多…

常用网址-2023整理

办公&效率人民币大写转换 人民币大写 人民币RMB数字转大写汉字工具我的账单 - 支付宝Bypass - 分流抢票Zen Flowchart - 在线流程图MindMaster - 在线思维导图【抠图】在线抠图软件_AI抠图证件照换底色-稿定设计Visio模板推荐与VisualNet图库转化语音转文字iconfont-阿里巴…

LeetCode动态规划经典题目(九):middle

学习目标&#xff1a; 进一步了解并掌握动态规划 学习内容&#xff1a; 4. LeetCode62. 不同路径https://leetcode.cn/problems/unique-paths/ 5. LeetCode63. 不同路径 IIhttps://leetcode.cn/problems/unique-paths-ii/ 6. LeetCode343. 整数拆分https://leetcode.cn/pro…

人工智能学习06--pytorch06--神经网络骨架nn.Module scipy下载 现有网络模型的使用及修改(VGG16)

神经网络骨架nn.Module 括号里nn.Module表示继承Module类init 初始化 调用父类初始化函数forward scipy下载 pip install scipy -i https://pypi.douban.com/simple/ 现有网络模型的使用及修改&#xff08;VGG16&#xff09; pretrained为True时需要下载&#xff0c;在ima…

1. Spring 基础入门

文章目录1. 初识 spring1.1 系统架构1.2 学习路线1.3 核心概念2. IoC 与 DI 入门案例&#xff08;xml版&#xff09;2.1 IoC&#xff08;控制反转&#xff09;2.2 DI&#xff08;依赖注入&#xff09;3. bean 配置3.1 bean 基础配置3.2 bean 别名配置3.3 bean 作用范围配置4. b…

file控件与input标签的属性type=“hidden“标签

<!DOCTYPE html> <html> <head> <meta charset"utf-8"> <title>file控件于与input标签的属性type"hidden"标签</title> </head> <body bgcolor"antiquewhite"> …

k8s中使用Deployment控制器实现升级、回滚、弹性伸缩

前置条件&#xff1a;linux机器已安装k8s集群基于yaml文件创建pod,本次创建pod使用的web.yaml如下apiVersion: apps/v1 kind: Deployment metadata:creationTimestamp: nulllabels:app: webname: web spec:replicas: 2selector:matchLabels:app: webstrategy: {}template:metad…

从零开始的数模(八)TOPSIS模型

一、概念 1.1评价方法概述 1.2概念 TOPSIS &#xff08;Technique for Order Preference by Similarity to an Ideal Solution &#xff09;模型中文叫做“逼近理想解排序方法”&#xff0c;是根据评价对象与理想化目标的接近程度进行排序的方法&#xff0c;是一种距离综合评…

SAP入门技术分享六:搜索帮助

搜索帮助1.概要&#xff08;1&#xff09;利用ABAP数据字典的搜索帮助&#xff08;2&#xff09;利用画面的搜索帮助&#xff08;3&#xff09;Dialog程序中的搜索帮助&#xff08;4&#xff09;报表选择屏幕PARAMETERS的搜索帮助&#xff08;5&#xff09;搜索帮助类型2.创建搜…

plot4gmns:面向通用建模网络范式(GMNS)的快速可视化【v0.1.1】

一款面向通用建模网络范式&#xff08;GMNS&#xff09;的快速可视化工具 目录1. 标准数据框架2. 标准数据框架下的生态2.1 数据解析2.2 数据处理2.3 数据可视化3. 标准数据框架下的可视化3.1 基础语法3.2 进阶语法1. 标准数据框架 制定一套标准的数据框架&#xff0c;可实现不…

python图像处理(中值滤波)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】 中值滤波和均值滤波的区别,有点像中位数收入和平均收入的区别。比如有三个人,年收入分别是10万、1万和1千,那么他们的平均收入就是(10+1+0.1)/3,平均数是3.3万左右,但是中位数…

《真象还原》读书笔记——第二章 编写 MBR 主引导记录

2.1 计算机的启动过程 开机后运行的第一个程序是 BIOS 。 BIOS 搬运 MBR 并 跳转运行 MBR… 2.2 软件接力第一棒 BIOS 全名 基本输入输出系统。 2.2.1 实模式下的 1MB 内存分布 2.2.2 BIOS 是如何苏醒的 BIOS本身不需要修改&#xff0c;于是被写入了ROM中&#xff0c;被映…

更换新电脑,如何将旧电脑数据/文件传输到新电脑?

最好的数据迁移工具提供了一种简单的解决方案&#xff0c;可将您的数据从一台 PC 传输到另一台 PC。 如果您以前没有做过&#xff0c;那么数据迁移的整个过程可能看起来很吓人。无论您是企业用户还是家庭用户&#xff0c;尝试将所有文​​件和文件夹从一台计算机迁移到另一台计…