搜索与图论2.2-Floyd算法

这篇具有很好参考价值的文章主要介绍了搜索与图论2.2-Floyd算法。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、简述

\(Floyd\) 算法是一种可以快速求解图上所有顶点之间最短路径的算法。

\(Bellman-Ford\)\(Dijkstra\) 算法求解的都是从一个起始点开始的最短路。如果想要求解图中所有顶点之间的最短路,就需要枚举每个点做为起点,这样十分低效。\(Floyd\) 算法(也称 \(Floyd-Warshall\) 算法)处理用邻接矩阵存储的有向图(无向图的一条边可以看做有向图的两条边)十分方便。

二、Floyd算法

memset(f, 127, sizeof f);
f[0][i][j] = a[i][j];

for (int k = 1; k <= n; k++)
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++)
			f[k][i][j] = min(f[k - 1][i][j], f[k - 1][i][k] + f[k - 1][k][j]);
  • \(f[k][i][j]\) 表示从 \(i\)\(j\) 并且可以经过 \(1\)\(k\) 的最短路径,\(f[0][i][j]\) 表示从 \(i\)\(j\) 并且不经过任何中间点的最短路径。
  • \(a[i][j]\) 表示从 \(i\)\(j\) 的边的长度,\(a[i][i]=0\)

\(Floyd\) 算法是动态规划的思想。在转移方程中,从 \(i\)\(j\) 的最短路径可以经过顶点 \(k\),也可以不经过顶点 \(k\),经过顶点 \(k\),则 \(k\) 将路径分为两段,前后两段最多只能经过 \(1\)\(k-1\),不经过顶点 \(k\),则最多经过 \(1\)\(k-1\)

那么上述代码可以空间优化吗?答案是肯定的,优化后的代码如下

memset(f, 127, sizeof f);
f[i][j] = a[i][j];

for (int k = 1; k <= n; k++)
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++)
			if (f[i][k] < 1 << 30 && f[k][j] < 1 << 30) // 注意int类型数据范围
				f[i][j] = min(f[i][j], f[i][k] + f[k][j]);

优化后,验证一下正确性,在加入顶点 \(k\) 之前,\(f[i][j]\) 就相当于 \(f[k-1][i][j]\),但是 \(f[i][k]\) 以及 \(f[k][j]\) 是有可能在 \(f[i][j]\) 之前被覆盖的,但是注意 \(f[k][i][j]\) 的含义为从 \(i\)\(j\) 并且可以经过 \(1\)\(k\) 的最短路径,那么 \(1\)\(k\) 为中间点,则 \(f[k-1][i][k]=f[k][i][k]\) 并且 \(f[k-1][k][j]=f[k][k][j]\),因此可以使用 \(f[i][k] + f[k][j]\) 来替换。

关于路径的记录,可以使用 \(path\) 数组在距离更新时来记录,\(path[i][j]=k\),表示从 \(i\)\(j\) 经过中间点 \(k\)(\(path[i][j]=-1\) 表示 \(i\)\(j\) 直接相连),然后利用 \(k\) 将路径分为两部分,依次递归输出。

模板题AcWing854.Floyd求最短路

题目描述

给定一个 \(n\) 个点 \(m\) 条边的有向图,图中可能存在重边和自环,边权可能为负数。
再给定 \(k\) 个询问,每个询问包含两个整数 \(x\)\(y\),表示查询从点 \(x\) 到点 \(y\) 的最短距离,如果路径不存在,则输出 impossible
数据保证图中不存在负权回路。

输入格式

第一行包含三个整数 \(n,m,k\)
接下来 \(m\) 行,每行包含三个整数 \(x,y,z\),表示存在一条从点 \(x\) 到点 \(y\) 的有向边,边长为 \(z\)
接下来 \(k\) 行,每行包含两个整数 \(x,y\),表示询问点 \(x\) 到点 \(y\) 的最短距离。

输出格式

\(k\) 行,每行输出一个整数,表示询问的结果,若询问两点间不存在路径,则输出 impossible

数据范围

\(1≤n≤200\)
\(1≤k≤n^2\)
\(1≤m≤20000\)
图中涉及边长绝对值均不超过 \(10000\)

输入样例
3 3 2
1 2 1
2 3 2
1 3 1
2 1
1 3
输出样例
impossible
1
C++代码
#include <bits/stdc++.h>
using namespace std;
const int N = 210, INF = 1e9;

int n, m, q;
int dist[N][N];

void floyd() {
    for (int k = 1; k <= n; k++)
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++)
                dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);
}

int main() {
    cin >> n >> m >> q;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++) {
            if (i == j) 
                dist[i][j] = 0;
            else 
                dist[i][j] = INF;
        }
    while (m--) {
        int x, y, z;
        cin >> x >> y >> z;
        dist[x][y] = min(dist[x][y], z);
    }
    floyd();
    while (q--) {
        int x, y;
        cin >> x >> y;
        if (dist[x][y] > INF / 2) 
            puts("impossible");
        else 
            cout << dist[x][y] << endl;
    }
    return 0;
}

三、Floyd算法的应用

这里给出一个和 \(Floyd\) 算法思想相关的题目。

Daimayuan Online Judge.删点游戏

题目描述

给你一张 \(n\) 个顶点的有向简单图,顶点编号从 \(1\)\(n\),我们要把所有顶点一个一个删完。小蜗每次会删掉图中的一个顶点和所有与它相连的边,小蜗想知道每删去一个顶点之后图中所有点对的距离之和。

输入格式

第一行一个整数 \(n\),表示顶点数。
接下来 \(n\) 行,每行 \(n\) 个整数,组成一个 \(n×n\) 的矩阵 \(A\) 表示这张图的邻接矩阵,矩阵第 \(i\) 行第 \(j\) 列表示 \(i\) 号顶点到 \(j\) 号顶点有一条边权为 \(A[i][j]\) 的边。
接下来一行,输入 \(n\) 个数 \(x_1,x_2,...,x_n\),代表删除顶点的顺序。

输出格式

输出一行 \(n\) 个数依次表示删除了第 \(0\) 个点(一个点都没删除)到第 \(n−1\) 个点(图中还剩下一个点)后,图中所有剩下的点对的距离之和。

数据范围

对于所有数据,保证 \(2≤n≤300,1≤A[i][j]≤10000,A[i][i]=0,1≤x_i≤n\)

输入样例
2
0 5
4 0
1 2
输出样例
9 0
解题思路

根据题目描述,按照给定的顺序删除顶点,计算剩余各点之间的最短路的距离和。在 \(Floyd\) 算法中,在求解过程中是依次枚举中间点 \(k\),那么就相当于每次都增加一个顶点,因此此问题可以逆向为按照给点的顺序依次增加顶点,计算图中已有顶点之间的最短路的距离和。
在顶点的增加中,利用布尔数组来表示某一顶点是否在图中,在计算最短路距离和的时候需要考虑当前的顶点是否在图中。文章来源地址https://www.toymoban.com/news/detail-711547.html

C++代码
#include <bits/stdc++.h> 
using namespace std;
const int N = 310, M = 65;
typedef long long LL;

int n;
int x[N], dist[N][N];
bool b[N];
int a[N];

int main() {
	scanf("%d", &n);
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++)
			scanf("%d", &dist[i][j]);
	for (int i = 1; i <= n; i++)
		scanf("%d", &x[i]);
	for (int l = n; l; l--) {
		int k = x[l];
		for (int i = 1; i <= n; i++)
			for (int j = 1; j <= n; j++)
				dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);
		b[k] = true;
		int ans = 0;
		for (int i = 1; i <= n; i++)
			for (int j = 1; j <= n; j++)
				if (b[i] && b[j])
					ans += dist[i][j];
		a[l] = ans;
	}
	for (int i = 1; i <= n; i++)
		printf("%d ", a[i]);
    return 0;
}

到了这里,关于搜索与图论2.2-Floyd算法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 算法基础课-搜索与图论

    题目链接:842. 排列数字 - AcWing题库 思路:写的很好的题解AcWing 842. 排列数字--深度优先遍历代码+注释 - AcWing 也可以考虑使用c++自带的next_permutation函数直接秒了: 题目链接:844. 走迷宫 - AcWing题库 思路:由于bfs是一层一层扩展,所以能保证走到终点时,走过的距离最短,所

    2024年04月15日
    浏览(42)
  • 搜索与图论(acwing算法基础)

    排列数字 n皇后 走迷宫 单链表 点击跳转至例题 idx存的是指针 树与图的深度优先搜索 树的重心 每个节点都是一个单链表 模拟队列 hh = 0 , tt = -1 有向图的拓扑序列 都是从前指向后,即有向无环图(不能有环) 所有入度为0的点,都能排在前面的位置 删掉t-j的边,仅仅是j的入度

    2024年02月08日
    浏览(33)
  • 搜索与图论:匈牙利算法

    将所有点分成两个集合,使得所有边只出现在集合之间,就是二分图 二分图:一定不含有奇数个点数的环;可能包含长度为偶数的环, 不一定是连通图

    2024年02月08日
    浏览(31)
  • acwing算法基础之搜索与图论--kruskal算法

    kruskal算法的关键步骤为: 将所有边按照权重从小到大排序。 定义集合S,表示生成树。 枚举每条边(a,b,c),起点a,终点b,边长c。如果结点a和结点b不连通(用并查集来维护),则将这条边加入到集合S中。 kruskal算法的时间复杂度为O(mlogm),它用来解决稀疏图的最小生成树问题

    2024年02月05日
    浏览(35)
  • 【算法基础:搜索与图论】3.3 拓扑排序

    https://oi-wiki.org/graph/topo/ 本文主要学习拓扑排序相关知识。 拓扑排序的英文名是 Topological sorting。 拓扑排序要解决的问题是给一个 有向无环图 的 所有节点排序 。 我们可以拿大学每学期排课的例子来描述这个过程,比如学习大学课程中有:程序设计,算法语言,高等数学,

    2024年02月16日
    浏览(35)
  • 【算法基础:搜索与图论】3.2 树与图的dfs和bfs

    要学会建树、建图的通用方法。 dfs 和 bfs 的代码框架。 https://www.acwing.com/problem/content/848/ 在 dfs 的过程中,统计各个节点作为断点时的连通块最大值。 https://www.acwing.com/problem/content/849/ 看到最短距离就可以想到使用宽搜。 注意! :题目中说明了 a 和 b 表示存在一条从 a 走到

    2024年02月16日
    浏览(29)
  • Acwing-基础算法课笔记之搜索与图论

    bellman-ford算法适用于负权边的图,求 1 到 n 的最多经过k条边的最短距离。 如图所示: 1 2 3 dist 0 ∞ infty ∞ ∞ infty ∞ ⇓ Downarrow ⇓ 1 2 3 dist 0 1 ∞ infty ∞ ⇓ Downarrow ⇓ 1 2 3 dist 0 1 2 此过程中出现了串联的结果,所以是错误的,此时需要进行备份操作。 备份操作如下: 为了

    2024年01月20日
    浏览(40)
  • acwing算法基础课(第三讲 搜索与图论)

    void dfs(int u){ if(n == u){ for(int i = 0;i n;i++) puts(g[i]); puts(“”); return; } for(int i = 0;i n;i++){ if(!col[i] !dg[u+i] !udg[n - u + i]){ g[u][i] = ‘Q’; col[i] = dg[u+i] = udg[n - u + i] = true; dfs(u+1); col[i] = dg[u+i] = udg[n - u + i] = false; g[u][i] = ‘.’; } } } int main(){ scanf(“%d”,n); for(int i = 0;i n;i++){ for(int j = 0;j

    2024年04月10日
    浏览(41)
  • 【AcWing算法基础课】第三章 搜索与图论

    本专栏文章为本人AcWing算法基础课的学习笔记,课程地址在这。如有侵权,立即删除。 特点 :尽可能先向 纵深方向 搜索。使用 stack 实现。所需空间 O(h) (h为深度)。不具有“最短性”。 题目链接 :842. 排列数字 1.1题目描述 给定一个整数 n,将数字 1∼n 排成一排,将会有

    2024年02月12日
    浏览(53)
  • 二十九、搜索与图论——克鲁斯卡尔算法(Kruskal 算法,稀疏图)

    解决问题: 多个城市中铺公路,使城市之间可以相互联通,问如何才能让铺设公路的长度最短——铺设的路径即为最小生成树。 思想: 从小到大枚举每条边,从小到大试图将每条边假如生成树,只要这条边对应的两个点不在一个集合,则把这条边加到集合中来。 主要面对的

    2024年02月05日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包