温故知新 —— Floyd算法
什么?Floyd?sb O(n ^ 3) 算法早不用了,右上角红叉吧。
我之前虽然也认识过 Floyd 算法的重要性,不过多少也是这么想的。
然而最近三天连续 rand 到了好几道有关的题目,让我彻底重新审视了 Floyd —— 既然能够作为一个重要的算法流传至今,那自有他的重要之处。
Floyd 是一个求解所有点对间的最短路算法,也可能是绝大多数人接触的最早的最短路算法。它适用于无负权边的图,时间复杂度约为 O(n ^ 3) 。因为时间复杂度太高了,所以也是很多人起初都对它有些成见的原因,再加上任意点对间最短路用的又少,会误认为这个算法后期就一无是处了。
众所周知,Floyd 的本质是动态规划。对于每一对顶点 u 和 v,看看是否存在一个顶点 w 使得从 u 到 w 再到 v 比己知的路径更短。如果是就更新它。正是由于这个动态规划思想的精髓所在,以及一层层更新的特性,使得 Floyd 大有用武之地。
废话不多说了,反正四行的 Floyd 没人不会……
万恶之源那天,我正在 Luogu 愉快地随机跳题,于是就 rand 到了 P2103 道路值守。
很开心,这不就是个最短路计数?正巧前一天就 A 了一道类似的题,很快就敲出来准备 AC 了。结果嘛……
怎么办,蒟蒻 Nanjo_Qi 瞬间就没有思路了,于是只能求助题解。
结果是个用到 Floyd 的题目。
Floyd 的重要特性是:全面枚举,有序更新。
Floyd题:https://www.luogu.org/problemnew/show/P2103。
这个也是:https://www.luogu.org/problemnew/show/P1476。
这个也是:https://www.luogu.org/problemnew/show/P1119。
P2103 道路值守:
利用 Floyd 层层松弛更新,以及循环遍历所有点对的特性,完全无需考虑时间复杂度;
首先求出所有点对间最短路,然后枚举寻找那些可以作为方案数加入的点(另一条与最短路长度相等,或中途汇入最短路的道路,以此形成可行方案),分别在每个汇入点记录,最后累计。
- #include <queue>
- #include <cstdio>
- #include <cstring>
- #include <algorithm>
- using namespace std;
- const int maxn = + ;
- int n, m, g[maxn][maxn], dis[maxn][maxn], method[maxn][maxn];
- int main(int argc, char const *argv[])
- {
- memset(dis, 0x3f, sizeof dis);
- scanf("%d%d", &n, &m);
- for(int i = ; i <= m; ++i) {
- int u = , v = , w = ;
- scanf("%d%d%d", &u, &v, &w);
- dis[u][v] = dis[v][u] = g[u][v] = g[v][u] = w;
- }
- for(int i = ; i <= n; ++i) dis[i][i] = ;
- for(int k = ; k <= n; ++k)
- for(int i = ; i <= n; ++i)
- for(int j = ; j <= n; ++j)
- dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
- for(int i = ; i <= n; ++i) {
- int tmp[maxn] = {};
- for(int j = ; j <= n; ++j) if( i != j && dis[i][j] != dis[][] )
- for(int k = ; k <= n; ++k) if( g[k][j] )
- if( dis[i][j] == dis[i][k] + g[k][j] ) ++tmp[j];
- for(int j = ; j <= n; ++j) if( i != j )
- for(int k = ; k <= n; ++k)
- if( dis[i][j] == dis[i][k] + dis[k][j] ) method[i][j] += tmp[k];
- }
- for(int i = ; i <= n; ++i)
- for(int j = i + ; j <= n; ++j)
- printf("%d ", method[i][j]);
- // printf("_______________________________________________\n");
- // printf("Process Exited Correctly With A Return Value 0.\n");
- // printf("All Rights Reserved By Kimitsu Nanjo In 2018.\n\n");
- return ;
- }
P1476 休息中的小呆:
求 1 到 n + 1 的最长路;
用 Dijkstra 不知道为什么跪在了记录路径上,还是用 Floyd 边枚举边输出。
- #include <queue>
- #include <cctype>
- #include <cstdio>
- #include <cstring>
- #include <algorithm>
- using namespace std;
- int n, m, dis[][];
- int main(int argc, char const *argv[])
- {
- scanf("%d%d", &n, &m);
- for(int i = ; i <= m; ++i) {
- int u = , v = , w = ;
- scanf("%d%d%d", &u, &v, &w);
- dis[u][v] = w;
- }
- for(int k = ; k <= n + ; ++k)
- for(int i = ; i <= n + ; ++i)
- for(int j = ; j <= n + ; ++j)
- if( i != j && j != k && dis[i][k] && dis[k][j] )
- dis[i][j] = max(dis[i][j], dis[i][k] + dis[k][j]);
- printf("%d\n", dis[][n + ]);
- for(int i = ; i <= n + ; ++i)
- if( dis[][i] + dis[i][n + ] == dis[][n + ] )
- printf("%d ", i);
- // printf("_______________________________________________\n");
- // printf("Process Exited Correctly With A Return Value 0.\n");
- // printf("All Rights Reserved By Kimitsu Nanjo In 2018.\n\n");
- return ;
- }
P1119 灾后重建:
一两个月前做的题,本质是最短路,那时却完全不知道这个时间限制怎么处理(那时的代码还是如此的丑);
依然是 Floyd,因为这道题保证修复时间和询问都是是递增的,所以就使 k 作为全局变量,每次 k 只以已经修复完成的村庄进行松弛更新,一旦发现村庄 k 的修复时间大于此时的时间,k 就停止自增,等待下一次询问。
- #include<cstdio>
- #include<cstring>
- int g[][], t[];
- int n, m, q, u, v, w, d, k;
- inline int min(int a, int b) {
- return a>b?b:a;
- }
- int main() {
- scanf("%d%d", &n, &m);
- memset(g, 0x3f, sizeof(g));
- memset(t, 0x3f, sizeof(t));
- for(int i=; i<n; ++i) {
- scanf("%d", &t[i]);
- g[i][i] = ;
- }
- for(int i=; i<m; ++i) {
- scanf("%d%d%d", &u, &v, &w);
- g[u][v] = g[v][u] = w;
- }
- scanf("%d", &q);
- for(int i=; i<q; ++i) {
- scanf("%d%d%d", &u, &v, &d);
- while( t[k]<=d ) {
- for(int i=; i<n; ++i) {
- for(int j=; j<n; ++j) {
- g[i][j] = min(g[i][j], g[i][k]+g[k][j]);
- }
- }
- ++k;
- }
- if( t[u]>d || t[v]>d || g[u][v]==0x3f3f3f3f ) {
- printf("-1\n");
- }
- else {
- printf("%d\n", g[u][v]);
- }
- }
- return ;
- }
另外,Floyd 还可以用来求最小环,一个环中的最大结点为 k,与他相连的两个点为 i,j,这个环的最短长度为 g[i][k] + g[k][j] + dis[i][j](i 到 j 的路径中,所有结点编号都小于 k 的最短路径长度)。根据 Floyd 的原理,在最外层循环做了 k-1 次之后,dist[i][j] 则代表了 i 到 j 的路径中,所有结点编号都小于 k 的最短路径。故该算法一定能找到图中最小环。代码如下:
- void floyd() {
- for(int k = ; k <= n; ++k) { // 求最小环,不包含第k个点
- for(int i = ; i < k; ++i) { // 到k-1即可
- for(int j = i + ; j < k; j++) // 到k-1即可
- mincircle = min(mincircle , dis[i][j] + g[i][k] + g[k][j]); //无向图
- }
- for(int i = ; i <= n; ++i) // 更新最短路
- for(int j = ; j <= n; ++j)
- dis[i][j] = min(dis[i][k] + dis[k][j] , dis[i][j]);
- }
- }
Floyd 还有很多用途,限于篇幅不再赘述了。
—— 还记得那天的景色,就像真的到达了那个世界。
温故知新 —— Floyd算法的更多相关文章
- 最短路径之Floyd算法
Floyd算法又称弗洛伊德算法,也叫做Floyd's algorithm,Roy–Warshall algorithm,Roy–Floyd algorithm, WFI algorithm. Floy ...
- 最短路径—Dijkstra算法和Floyd算法
原文链接:http://www.cnblogs.com/biyeymyhjob/archive/2012/07/31/2615833.html 最后边附有我根据文中Dijkstra算法的描述使用jav ...
- 最短路径问题——floyd算法
floyd算法和之前讲的bellman算法.dijkstra算法最大的不同在于它所处理的终于不再是单源问题了,floyd可以解决任何点到点之间的最短路径问题,个人觉得floyd是最简单最好用的一种算法 ...
- floyd算法小结
floyd算法是被大家熟知的最短路算法之一,利用动态规划的思想,f[i][j]记录i到j之间的最短距离,时间复杂度为O(n^3),虽然时间复杂度较高,但是由于可以处理其他相似的问题,有着广泛的应用,这 ...
- Uvaoj 10048 - Audiophobia(Floyd算法变形)
1 /* 题目大意: 从一个点到达另一个点有多条路径,求这多条路经中最大噪音值的最小值! . 思路:最多有100个点,然后又是多次查询,想都不用想,Floyd算法走起! */ #include< ...
- Floyd算法(三)之 Java详解
前面分别通过C和C++实现了弗洛伊德算法,本文介绍弗洛伊德算法的Java实现. 目录 1. 弗洛伊德算法介绍 2. 弗洛伊德算法图解 3. 弗洛伊德算法的代码说明 4. 弗洛伊德算法的源码 转载请注明 ...
- Floyd算法(二)之 C++详解
本章是弗洛伊德算法的C++实现. 目录 1. 弗洛伊德算法介绍 2. 弗洛伊德算法图解 3. 弗洛伊德算法的代码说明 4. 弗洛伊德算法的源码 转载请注明出处:http://www.cnblogs.c ...
- Floyd算法(一)之 C语言详解
本章介绍弗洛伊德算法.和以往一样,本文会先对弗洛伊德算法的理论论知识进行介绍,然后给出C语言的实现.后续再分别给出C++和Java版本的实现. 目录 1. 弗洛伊德算法介绍 2. 弗洛伊德算法图解 3 ...
- 最短路径---Dijkstra/Floyd算法
1.Dijkstra算法基础: 算法过程比prim算法稍微多一点步骤,但思想确实巧妙也是贪心,目的是求某个源点到目的点的最短距离,总的来说dijkstra也就是求某个源点到目的点的最短路,求解的过程也 ...
随机推荐
- How to blog on Github
git clone https://github.com/test/test.github.io.git cd ~/test.github.io git config --global push.de ...
- mysql GTID主从配置
主数据库配置 [mysqld] server_id=1 gtid_mode=on enforce_gtid_consistency=on skip_slave_start=1log_bin=maste ...
- MD5进行解密操作
package com.dyy.test; import java.security.MessageDigest; public class TestMD5Util { /*** * MD5加码 生成 ...
- BZOJ3790神奇项链——manacher+贪心
题目描述 母亲节就要到了,小 H 准备送给她一个特殊的项链.这个项链可以看作一个用小写字 母组成的字符串,每个小写字母表示一种颜色.为了制作这个项链,小 H 购买了两个机器.第一个机器可以生成所有形式 ...
- My Brute HDU - 3315(KM || 费用流)
题意: 有S1到Sn这n个勇士要和X1到Xn这n个勇士决斗,初始时,Si的决斗对象是Xi. 如果Si赢了Xi,那么你将获得Vi分,否则你将获得-Vi分. Si和Xi对决时,Si有初始生命Hi,初始攻击 ...
- eclipse中git推送上传错误 没有足够的数据写入
Can't connect to any repository: https://github.com/jiashubing/test.git (https://github.com/jiashubi ...
- 爬虫_豆瓣全部正在热映电影 (xpath)
单纯地练习一下xpath import requests from lxml import etree def get_url(url): html = requests.get(url) retur ...
- python学习日记(深浅copy)
赋值 #赋值,指向同一内存地址 l1 = [1,2,3,4,5] l2 = l1 print(l1,l2) print(id(l1),id(l2)) 浅copy #浅copy,第一层互相独立,创建了新 ...
- 滚动ListView时图像顺序混乱
本文选自StackOverflow(简称:SOF)精选问答汇总系列文章之一,本系列文章将为读者分享国外最优质的精彩问与答,供读者学习和了解国外最新技术.本文将为读者讲解滚动ListView时图像顺序混 ...
- 各种“地”—— 各种“GND”
GND,指的是电线接地端的简写.代表地线或0线. 电路图上和电路板上的GND(Ground)代表地线或0线.GND就是公共端的意思,也可以说是地,但这个地并不是真正意义上的地.是出于应用而假设的一个地 ...