图论——最短路 学习笔记

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

图论——最短路 学习笔记

其实是复习性质的,主要是总结,证明什么的,等上大学再说。

定义

  • 单源最短路:从一个点 \(q\) 出发,到其他所有点的最短路。
  • 全源最短路:任意两点见最短路。

算法对比

算法 Floyd Johnson Bellman–Ford SPFA Dijkstra
类型 全源 全源 单源 单源 单源
作用于 任意图 任意图 任意图 任意图 非负权图
检测负环 不能
时间复杂度 \(\mathcal{O}(n^3)\) \(\mathcal{O}(nm \log m)\) \(\mathcal{O}(nm)\) \(\mathcal{O}(m)\)\(\mathcal{O}(nm)\) \(\mathcal{O}(n^2)\)\(\mathcal{O}(m \log n)\)

总结:

  • 没有负环用 dijk,理论上,稠密图(有 \(m\)\(n^2\) 同阶)用朴素的,稀疏图(有 \(m \ll n^2\) 的)用堆优化。
  • 有负环优先用 SPFA,即使她被卡也比 BF 快一点。
  • 多源用 Floyd,因为不会 Johnson。

前言

如果是 DAG 也可以跑拓扑,速度更快!

Dijkstra

过程

设两个集合:「已确定最短路长度的集合 \(S\)」和「未确定最短路长度的集合 \(T\)」,每次从 \(T\) 中选取一个最近的,加入集合 \(S\) 并松弛,更新其他点的最短路。直到 \(T\) 集合为空。

代码:

int n, m, g[N][N];
array<int, N> dis, vis;	// 到这个点的最短路,是否已经确定最短路
int gett() {
	int t = -1;
	for (int i = 1; i <= n; ++j)
	if (!st[i] && (t == -1 || dis[t] > dis[i])) t = i;
	return t;
} void dijkstra(int s) {
	dis.fill(0x3f); dis[1] = 0;
	for (int i = 0; i < n; ++i) {
		int t = gett(); st[t] = true;
		for (int j = 1; j <= n; ++j)
		dist[j] = min(dist[j], dist[t] + g[t][j]);
	} return (void)("rp++");
}

时间复杂度:\(\mathcal{O}(n^2)\)

堆优化

每成功松弛一条边 \((u,v)\) ,就将 \(v\) 插入堆中,如果 \(v\) 已经在堆中,直接修改相应元素的权值即可,每次查找操作直接取堆顶结点即可。

代码:

using pii = pair<int, int>;
array<int, N> dis, st;	// 到这个点的最短路,是否在堆中
#define v e[i]
void dijkstra(int s) {
	dis.fill(0x3f); dis[s] = 0;
	priority_queue<pii, vector<pii>, greater<pii>> heap;
	heap.push({0, s}); while (heap.size()) {
		pii t = heap.top(); heap.pop();
		int u = t.second, d = t.first;
		if (st[u]) continue; st[u] = true;
		for (int i = h[u]; i != -1; i = ne[i])
		if (dist[v] > d + w[i]) dis[v] = d + w[i], heap.push({dis[v], v});
	} return (void)("rp++");
}

共计 \(\mathcal{O}(m)\) 次二叉堆上的插入操作,\(\mathcal{O}(n)\) 次删除堆顶操作,而插入和删除的时间复杂度均为 \(\mathcal{O}(\log n)\),故时间复杂度:\(\mathcal{O}((m+n) \log n)=\mathcal{O}(m \log n)\)

Bellman–Ford

不断尝试对图上每一条边进行松弛。我们每进行一轮循环,就对图上所有的边都尝试进行一次松弛操作,当一次循环中没有成功的松弛操作时,算法停止。

因为一个图的最短路,在没有负环的情况下,最长只能是 \(n-1\) 条边,所以松弛 \(n-1\) 轮即可。

因此可以得出此算法求不超过 \(k\) 条边的最短路的方法,即松弛 \(k\) 轮。

int n, m, k;
struct Edge { int a, b, w; } edges[M];
array<int, N> dis;
void bellman_ford(int s) {
	dis.fill(INF); dis[s] = 0;
	for (int i = 0; i < k; ++i) { // 不超过 k 条边的最短路
		auto bak = dis;	// 需要备份下来处理
		bool flag = false;
		for (int j = 0 ; j < m ; ++j)
		if (bak[edges[j].a] + edges[j].w < dis[edges[j].b])
			dis[edges[j].b] = bak[edges[j].a] + edges[j].w, flag = true;
		if (!flag) break;
	}
}

在最短路存在的情况下,由于一次松弛操作会使最短路的边数至少 \(+1\),而最短路的边数最多为 \(n-1\),因此整个算法最多执行 \(n-1\) 轮松弛操作。故总时间复杂度为 \(\mathcal O(nm)\)

队列优化 Bellman–Ford——SPFA

SPFA 说的通俗点叫 Bellman–Ford 的队列优化(BFS)版。

原理是,只有上一次被松弛的结点,所连接的边,才有可能引起下一次的松弛操作。

那么我们用队列来维护「哪些结点可能会引起松弛操作」,就能只访问必要的边了。

代码:

int h[N], w[N], e[N], ne[N], idx;
array<int, N> dis, st;	// 到这个点的最短路,是否在队列中
#define v e[i]
void spfa(int s) {
	dis.fill(INF); dist[1] = 0;
	queue<int> q; q.push(1);
	st[1] = true;
	while (q.size()) {
		int u = q.front(); q.pop();
		st[u] = false;
		for (int i = h[u] ; i != -1 ; i = ne[i])
		if (dist[v] > dis[u] + w[i]) {
			dist[v] = dis[u] + w[i];
			if (!st[v]) q.push(v), st[v] = true;
		}
	}
}

栈优化 Bellman–Ford——找负环

通常用于判负环,不用像 SPFA 那样判进队次数的原因是:DFS 在栈内的只有祖先,而 BFS 有非祖先。

int dis[N], vis[N]; // 提前将 dis 赋为 INF
bool dfs_spfa(int u) {
	if (vis[u]) return true; vis[u] = true;
	for (int i = h[u]; i != -1; i = ne[i])
	if (dis[e[i]] > dis[u] + w[i]) {
		dis[e[i]] = dis[u] + w[i];
		if (dfs_spfa(e[i])) return true;
	} return vis[u] = false;
}

Floyd

一个很实用的全源最短路解法,特点是好写,容易拓展。

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

代码:

for (k = 1; k <= n; k++)
	for (x = 1; x <= n; x++)
		for (y = 1; y <= n; y++)
			f[x][y] = min(f[x][y], f[x][k] + f[k][y]);

Johnson

不会,现阶段不打算学。

Reference

[1] https://oi-wiki.org/graph/shortest-path/文章来源地址https://www.toymoban.com/news/detail-746187.html

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

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

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

相关文章

  • 图论——最短路径

    Dijkstra’s algorithm(迪杰斯特拉算法)是一种用于解决图中单源最短路径问题的贪婪算法。该算法由荷兰计算机科学家Edsger Dijkstra于1956年提出。它主要用于计算从一个起始顶点到图中所有其他顶点的最短路径。 算法步骤如下: 初始化: 创建一个集合S,用于存储已找到最短路

    2024年02月21日
    浏览(40)
  • 【图论】最短路的传送问题

    P4568 [JLOI2011] 飞行路线 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 可知背景就是求最短路问题,但难点是可以使一条路距离缩短至0,那如何更好的利用这个机会呢? 此时我们可以用到分层图,如下: 即我们可以免费往下传一次,其实也就相当于两点距离为0了,这时终点应该

    2024年02月12日
    浏览(36)
  • 图论--最短路问题

    邻接表-链式前向星 1.拓扑排序 给定一个 n 个点 m 条边的有向图,点的编号是 11 到 n,图中可能存在重边和自环。 请输出任意一个该有向图的拓扑序列,如果拓扑序列不存在,则输出 −1−1。 若一个由图中所有点构成的序列 A 满足:对于图中的每条边 (x,y),x在 A中都出现在

    2024年02月14日
    浏览(36)
  • 【图论】最短路算法

    不能处理边权为负的情况, 复杂度O(nlogn) 步骤与基本思路 (1)初始化距离数组dist[N],将其所有值赋为0x3f,并将起点1的dist初始化为0,存入优先队列heap中 (2)从所有 未被遍历 的点中找到与起点1的 距离dist[i]最小 的点,并将该点标记为已遍历 (3)利用刚刚遍历的这个点

    2024年02月16日
    浏览(39)
  • 图论---最短路径问题

            解决图论问题中的最短路径问题一般有四种算法,分别是Floyd算法、Dijkstra算法、Bellman-Ford算法和SPFA算法,下面介绍一下这几种算法的模板和原理用途。 Floyd算法 原理:Floyd本质上是一个 动态规划 的思想,每一次循环更新 经过前k个节点,i到j的最短路径 。 用途

    2024年02月08日
    浏览(26)
  • 图论 <最短路问题>模板

    有向图 1.邻接矩阵,稠密图 2.邻接表 (常用)单链表,每一个点都有一个单链表 ,插入一般在头的地方插, 图的邻接表的存储方式 树的深度优先遍历 特殊的深度优先搜索,难点是如何实现,一条道走到黑 树的宽度优先遍历 例题:图的层序搜索 拓扑序列(有向图) 例题

    2024年02月14日
    浏览(27)
  • 图论---最短路问题

    单源最短路 n: 点的数量 m: 边的数量 所有边权都是正数 (1)朴素Dijkstra算法 O(n^2) (2)堆优化版的Dijkstra算法 O(mlogn) 存在负权边 (1)Bellmax-Fold O(nm) (让选择不超过k条边的时候使用) (2)SPFA 一般O(m),最坏O(nm) 多源汇最短路 Floyd算法 O(n^3) 每次找到距离起点最近的点,然后用这个点去更新

    2024年02月05日
    浏览(30)
  • 【图论】单源最短路

    算法提高课笔记 最短路问题可以分为以下两类: 边权非负——朴素Dijkstra、堆优化Dijkstra 有负权边——Bellman-Ford、SPFA 热浪 原题链接 德克萨斯纯朴的民众们这个夏天正在遭受巨大的热浪!!! 他们的德克萨斯长角牛吃起来不错,可是它们并不是很擅长生产富含奶油的乳制品

    2024年02月14日
    浏览(43)
  • 图论(二)之最短路问题

    栗题 https://www.acwing.com/problem/content/851/ https://www.acwing.com/problem/content/852/ 思想 迪杰斯特拉算法采用的是一种贪心的策略。 求源点到其余各点的最短距离步骤如下: 用一个 dist 数组保存源点到其余各个节点的距离,dist[i] 表示源点到节点 i 的距离。初始时,dist 数组的各个元素

    2024年04月13日
    浏览(38)
  • 图论————最短路

    最短路 Dijkstra-朴素 O(n^2) 初始化距离数组, dist[1] = 0, dist[i] = inf; for n次循环 每次循环确定一个min加入S集合中,n次之后就得出所有的最短距离 将不在S中dist_min的点-t t-S加入最短路集合 用t更新到其他点的距离 Dijkstra-堆优化 O(mlogm) 利用邻接表,优先队列 在priority queue[HTML REMOV

    2024年02月15日
    浏览(27)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包