Floyd算法及其应用

Part I-Introduction

Floyd算法是一种求图上多源最短路径的算法,适用于中小规模的图,思维简单易懂。

Floyd算法的实质是(区间)动态规划,在这里做一个简单的概述。

对于一个有\(n\)个结点的图,

\(dis[i][j]\)为结点\(i\)到结点\(j\)的最短路径长度。

首先,将所有现成的边都存入\(dis\),其余的令其值\(=\infty\),并使\(dis[i][i]=0\)

接着,枚举中转点(\(k\)),那么:

\[dis[i][j]=\min\{dis[i][k]+dis[k][j]\text{ | }k\in[1,n],k\ne i,k\ne j\}\]

代码实现:

void Floyd()
{memset(dis,0x3f3f3f3f,sizeof(dis));for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)if(a[i][j]!=0x3f3f3f3f)边(i,j)存在。dis[i][j]=a[i][j];//a为现存的边。for(int i=1;i<=n;i++) dis[i][i]=0;for(int k=1;k<=n;k++)//枚举中转点。for(int i=1;i<=n;i++)for(int j=1;j<=n;j++){if(i==j||j==k||k==i) continue;dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);}
}

Attention:循环时\(k\)必须放在第一层。若将\(i\)置于第一层,就会导致i->k->j的距离过早的确定下来,可能会导致答案错误。

注:其实最原始的Floyd中\(dis\)的定义是:\(dis[i][j][k]\)表示结点\(i\)到结点\(j\)只经过结点\(i-k\)的最短路径。 则有:\(dis[i][j][k]=min(dis[i][j][k-1],dis[i][k][k-1]+dis[k][j][k-1])\),降维得到现在的方程。

时间复杂度:\(O(n^3)\)

Part II-Sevral Simple Problems

Floyd算法可以解决许多看似无法处理的问题。

Problem[1]:[USACO09JAN] Best Spot

链接:https://www.luogu.org/problemnew/show/P2935

题面:

ZgPxGd.png

此题较为简单,算法流程:

  • Floyd处理出各个点间的最短路径。

  • 计算出每个点到各个Favorites的总距离。

  • 选出总距离最小的点输出即可。

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define R registerint a[501][501];
int dis[501][501];
int fav[501];
int P,F,C;void Floyd()
{memset(dis,0x3f3f3f3f,sizeof(dis));for(R int i=1;i<=P;i++)for(R int j=1;j<=P;j++)if(a[i][j]!=0x3f3f3f3f)dis[i][j]=a[i][j];for(R int i=1;i<=P;i++) dis[i][i]=0;for(R int k=1;k<=P;k++)for(R int i=1;i<=P;i++)for(R int j=1;j<=P;j++){if(i==j||j==k||k==i) continue;dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);}
}signed main()
{scanf("%d%d%d",&P,&F,&C);memset(a,0x3f3f3f3f,sizeof(a));for(R int i=1;i<=F;i++) scanf("%d",fav+i);for(R int u,v,w,i=1;i<=C;i++){scanf("%d%d%d",&u,&v,&w);a[u][v]=a[v][u]=min(a[u][v],w);}Floyd();int minnum=0x3f3f3f3f,minid;for(R int i=1,sum=0;i<=P;i++,sum=0){for(R int j=1;j<=F;j++)sum+=dis[i][fav[j]];if(sum<minnum) minnum=sum,minid=i;}printf("%d\n",minid);return 0;} 

Problem[2]:[JSOI2007]重要的城市

链接:https://www.luogu.org/problemnew/show/P1841

题面:

Zg85Js.png

这一题比上一题略难,如何记录中转点?

Of course,在Floyd的时候。

\(c[i][j]\)为结点\(i,j\)之间的最短路的中转点,若无则为0;

在进行对\(dis[i][j]\)的更新之时,我们不直接取min,而是先判断以避免覆盖。

因为我们还要进行一个更重要的操作,that is,更新\(c[i][j]\)

分情况讨论:

  • 1.\(dis[i][j]>dis[i][k]+dis[k][j]\)(需要更新):此时结点\(i,j\)之间的最短路的中转点就要发生改变,即\(c[i][j]=k\),并更新\(dis[i][j]\)的值。

  • 2.\(dis[i][j]=dis[i][k]+dis[k][j]\) :这不仅说明原先的\(c[i][j]\)已经失效,而且意味着此时已经不存在\(c[i][j]\)了(并不需要中转点就有最短路了)。因此令\(c[i][j]=0\)

  • 3.\(dis[i][j]<dis[i][k]+dis[k][j]\):此时的中转点无法得到更优的解,忽略。

这样我们就处理好了\(c\)。对于结果的处理,可以利用桶排序的思想,令\(res[c[i][j]]=1\)。(res[N]:bool)

最后遍历\(res\),输出答案(别忘了无解的处理!!!)。

#include<cstdio>
#include<bitset>
#include<cstring>
using namespace std;const int N=205;
int c[N][N];
int f[N][N];
int n,m;void Solve()
{for(int i=1;i<=n;i++) f[i][i]=0;for(int k=1;k<=n;k++)for(int i=1;i<=n;i++)for(int j=1;j<=n;j++){if(i==j||j==k||i==k) continue;if(f[i][j]>f[i][k]+f[k][j])f[i][j]=f[i][k]+f[k][j],c[i][j]=k;else if(f[i][j]==f[i][k]+f[k][j])c[i][j]=0;}
}bool res[N],flag=0;
signed main()
{memset(f,0x3f3f3f3f,sizeof(f));memset(c,0,sizeof(c));scanf("%d%d",&n,&m);for(int u,v,w,i=1;i<=m;i++){scanf("%d%d%d",&u,&v,&w);f[u][v]=f[v][u]=w;//f:=dis}Solve();for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)res[c[i][j]]=1;for(int i=1;i<=n;i++)if(res[i]) printf("%d ",i),flag=1;if(!flag) puts("No important cities.");return 0;
}

Problem[3]:[NOI2007]社交网络

链接:https://www.luogu.org/problemnew/show/P2047

题面:

ZgJVBT.png

这题貌似比上一题更复杂——要进行路径计数。

\(cnt[i][j]\)为结点\(i,j\)之间的最短路径条数

还是在处理\(dis\)的时候处理\(cnt\)

分类讨论之前,你需要知道一件事情:

假设我们已经处理完了\(cnt[i][k]\)\(cnt[k][j]\),那么怎么知道\(cnt[i][j]\)的值? 对于\(cnt[i][k]\)中的每条路径,与\(cnt[k][j]\)配对,有\(cnt[k][j]\)条路径。 那么\(cnt[i][k]\)条一起配对就是\(cnt[i][k]\times cnt[k][j]\)条,这就是\(cnt[i][j]\)的值。(说白了就是乘法原理)

那么,再开始分类讨论

  • 1.\(dis[i][j]=dis[i][k]+dis[k][j]\):又找到了一坨解,\(cnt[i][j]+=cnt[i][k]*cnt[k][j]\)(注意是+=!)。

  • 2.\(dis[i][j]>dis[i][k]+dis[k][j]\) :这代表有了新的解,原先的答案不能再用了,\(cnt[i][j]=cnt[i][k]*cnt[k][j]\)(注意是=!)。

  • 3.\(dis[i][j]<dis[i][k]+dis[k][j]\):此时的中转点无法得到更优的解,忽略。

处理完\(cnt\)后,那就意味着\(C_{s,t},C_{s,t}(v)\)什么的都出来了。

P.S.:建议cnt用double

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;const int N=105;
int dis[N][N];
double cnt[N][N];
int n,m;signed main()
{memset(dis,0x3f3f3f3f,sizeof(dis));memset(cnt,0,sizeof(cnt));scanf("%d%d",&n,&m);for(int u,v,w,i=1;i<=m;i++){scanf("%d%d%d",&u,&v,&w);dis[u][v]=dis[v][u]=min(w,dis[u][v]);cnt[u][v]=cnt[v][u]=1;}for(int i=1;i<=n;i++)dis[i][i]=0;for(int k=1;k<=n;k++)for(int i=1;i<=n;i++)for(int j=1;j<=n;j++){if(i==j||i==k||j==k) continue;if(dis[i][j]==dis[i][k]+dis[k][j])cnt[i][j]+=cnt[i][k]*cnt[k][j];else if(dis[i][j]>dis[i][k]+dis[k][j]){dis[i][j]=dis[i][k]+dis[k][j];cnt[i][j]=cnt[i][k]*cnt[k][j];}}for(int k=1;k<=n;k++){double ans=0;for(int i=1;i<=n;i++)for(int j=1;j<=n;j++){if(i==j||i==k||j==k) continue;if(dis[i][j]==dis[i][k]+dis[k][j])ans+=cnt[i][k]*cnt[k][j]*1.0/cnt[i][j];}printf("%.3lf\n",ans);}
}

Part III-EXT:最小环问题

来看这么一个问题:http://acm.hdu.edu.cn/showproblem.php?pid=1599

题面:

Z2P9oD.png

看似无从下手,但仍然是Floyd

在更新\(dis\)前,我们已经把\(1-(k-1)\)的情况处理好了。那么当前的最小环就是:

\[\min\{a[i][k]+a[k][j]+dis[i][j]\}\]

先贴代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;const int N=105;
int a[N][N];
int dis[N][N];
int n,m;long long solve()
{long long min_circle=0x3f3f3f3f;for(int k=1;k<=n;k++){for(int i=1;i<k;i++)for(int j=i+1;j<k;j++)min_circle=min(min_circle,1ll*a[i][k]+a[k][j]+dis[i][j]);for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);}return min_circle;
}signed main()
{while(scanf("%d%d",&n,&m)!=EOF){memset(a,0x3f3f3f3f,sizeof(a));for(int u,v,w,i=1;i<=m;i++){scanf("%d%d%d",&u,&v,&w);a[u][v]=a[v][u]=min(a[u][v],w);}for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)dis[i][j]=a[i][j];long long res=solve();if(res!=0x3f3f3f3f) printf("%lld\n",res);else puts("It's impossible.");}return 0;
}

值得注意的是,\(i,j\)的循环范围的控制,因为\(i,j,k\)不能相同。

至于为什么先处理最小环在更新路径,当然是为了使\(dis[i][j]\)不经过\(k\)啊。

转载于:https://www.cnblogs.com/-Wallace-/p/11165803.html

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

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

相关文章

软件设计师--最早开始时间和最晚开始时间

题目如图所示&#xff0c;解法如下&#xff1a; 方法&#xff1a; 先求最早开始时间&#xff1a;A是开始节点&#xff0c;所以A的最早开始时间是0&#xff0c;并且最早开始时间等于最晚开始时间。等得到图中红色的部分。 其他节点的最早开始时间为以该节点作为弧头的所有有向…

声笔双拼单字效率分析

-----------------------声笔双拼单字效率分析-------------------------- 2 keys: 21 items, 99131909.00 times, covering 17.48 % 2 keys: 21 items, 99131909.00 times, covering 17.48 % 3 keys: 1774 items, 371822394.00 times, c…

软件设计师 --哈夫曼树的一个经典问题

题目如下&#xff1a;有很多人反应&#xff0c;他们怎么做都做不出正确的答案&#xff0c;结果发过他们画的哈夫曼树的图以后&#xff0c;发现图完全是错误的&#xff1b; 如下图所示&#xff1a;为什么错误的&#xff0c;因为在遇到有两个权重为17的树的时候&#xff0c;没有遵…

谈谈枚举和 那啥 那啥来着 哦 对对对 泛型!

枚举类型 是 用于声明一组命名的常数 的 基本数据类型&#xff08;值类型&#xff09;。enum <enum_name> { enumeration list }; 这个就是枚举一般用法。其中&#xff0c;enum_name 指定枚举的类型名称。enumeration list 是一个用逗号分隔的标识符列表。枚举列表中…

python3 shutil模块

# -*- coding:utf-8 -*- # Author: Evan Mi import shutil """ 主要作用是拷贝文件、拷贝文件的权限、状态信息以及压缩文件、移动文件、删除文件 """# 将一个文件对象的内容拷贝到另一个文件对象中,可以部分内容 """ f1 open(r…

mini2440烧写nor flash

1. 安装Setup_JLinkARM_V440.exe 2. 打开JLINK ARM 3. File->Open Project&#xff0c;打开 s3c2440a_embedclub.jflash4. Options->Project settings... 选择 Flash&#xff0c;点击 Select flash device&#xff0c;选中开发板对应的 Nor Flash 芯片型号。比 如 S29AL0…

软件设计师--判定覆盖,判定条件覆盖,条件组合覆盖--一个栗子

针对上图的一个判断条件&#xff0c;在这里将分别讨论判定覆盖、判定条件覆盖、条件组合覆盖的情况&#xff1a; 设T1A>3,T2B>3;为该判定节点的两个子条件。 (一&#xff09;判定覆盖&#xff1a; 所谓的判定覆盖就是让判定的真分支和假分支各执行一次&#xff0c;只要…

sqliteman install parameter

1、安装前准备系统要求&#xff1a;linuxQt库版本&#xff1a;一般都有2、安装文件 官网自行下载3、安装1&#xff09;这里用的pscp pscp .\sqliteman-1.2.2.tar.gz root192.168.30.140:/root/from_pscp2&#xff09;tar xf sqliteman-1.2.2.tar.gz3&#xff09;执行 cmake -D…

python3 多继承搜索__init__方法的两种策略

继承情形一&#xff1a;测试代码如下&#xff1a; class A(object):def __init__(self):print(A)class B(object):def __init__(self):print(B)class C(A):def __init__(self):print(C)class D(B): def __init__(self):print(D)class E(C, D):pass执行当前代码 xE()&#xff0c…

Unity Shader 屏幕后效果——Bloom外发光

Bloom的原理很简单&#xff0c;主要是提取渲染图像中的亮部区域&#xff0c;并对亮部区域进行模糊处理&#xff0c;再与原始图像混合而成。 一般对亮部进行模糊处理的部分采用高斯模糊&#xff0c;关于高斯模糊&#xff0c;详见之前的另一篇博客&#xff1a; https://www.cnblo…

python3 一些常用的数学函数

# -*- coding:utf-8 -*- # Author: Evan Mi import math# 返回数字的绝对值&#xff0c;如abs(-10)返回10 print(abs(-10)) # 返回数字向上取整的结果 print(math.ceil(1.3)) # 比较两个数 import operator print(operator.eq(1, 1)) print(operator.ne(1, 1)) print(operator.…

Gauss elimination Template

Gauss elimination : #include <iostream> #include <cstdlib> #include <cstring> #include <stdio.h> using namespace std;const int MAXN 50;int a[MAXN][MAXN];//增广矩阵 int x[MAXN];//解集 bool free_x[MAXN];//标记是否是不确定的变元 int f…

不要假装努力,结果不会陪你演戏!

转载于:https://www.cnblogs.com/strive-19970713/p/11171205.html

机器学习基石作业一中的PLA和POCKET_PLA实现

前提&#xff1a;文中使用的数据是本人下载下来以后自己处理过的&#xff0c;就是把文件中的所有分隔符都换成了空格。所以load_data方法只能加载我自己的数据&#xff0c;想要加载原生数据的话需要自己写load_data方法。 两个算法的关键地方都需要判断当前的w在某个样本点x上是…

机器学习基石-作业二-第10题分析

题目如上图所示&#xff0c;答案是&#xff1a;&#xff1b;在网上看到的答案中有一个很好的解释就是说在一个n纬的欧几里德空间里&#xff0c;分别按照参数做一个垂直于每个轴的超平面&#xff0c;这些超平面能够打散这么多个点。首先我承认这个事实&#xff0c;具体的证明还没…

用CSS控制Table和div因撑开而变形

用CSS控制Table和div因撑开而变形 http://inrainight.blogbus.com/logs/50853786.html <style>table { table-layout: fixed; word-wrap:break-word; }div { word-wrap:break-word; }</style> 语法&#xff1a; word-wrap : normal | break-word 参数&#xff1…

机器学习基石作业二中的DECISION_STUMP实现

概要&#xff1a;在林老的题目描述中&#xff0c;DECISION_STUMP&#xff08;其实就是“决策桩”&#xff0c;也就是只有一层的决策树&#xff09;。题目中提到了的选去是把属性&#xff08;一维的&#xff09;按照从小到大的顺序排列以后取两个挨着的值的平均值&#xff0c;网…

【MM系列】SAP 关于更改物料的价格控制类型

公众号&#xff1a;SAP Technical本文作者&#xff1a;matinal原文出处&#xff1a;http://www.cnblogs.com/SAPmatinal/ 原文链接&#xff1a;【MM系列】SAP 关于更改物料的价格控制类型前言部分 大家可以关注我的公众号&#xff0c;公众号里的排版更好&#xff0c;阅读更舒适…