A 星算法

A 星和 Dijkstra 算法唯一区别在于堆中排序的依据。distance 数组仍然保存实际代价,预估代价只影响堆的弹出顺序。

  • Dijkstra 根据源点到当前点的实际代价进行排序。

  • A 星根据源点到当前点的实际代价 + 当前点到终点的预估代价进行排序

预估函数要求:当前点到终点的预估代价 <= 当前点到终点的实际代价,越接近越快

常用选择:曼哈顿距离、欧氏距离、对角线距离(行差值列差值绝对值的最大值)

Floyd 算法

Floyd 算法是一种用于解决所有节点对之间最短路径问题的算法。它通过动态规划的思想,逐步计算出所有节点对之间的最短路径。

Floyd 算法使用一个二维数组 distance 来记录节点对之间的最短距离。初始时,distance[i][j] 表示节点 i 到节点 j 的直接距离(如果存在边),否则为无穷大。算法通过三重循环不断更新 distance 数组,最终得到所有节点对之间的最短路径。

Floyd 算法的核心思想是动态规划。外层循环控制中间节点 k,内层两个循环分别遍历起点 i 和终点 j。如果通过节点 k 可以使 i 到 j 的距离更短,则更新 distance[i][j]。重复此过程,直到所有节点都被遍历过。

diatance[i][j] 表示 i 和 j 的最短距离,更新:distance[i][j] = min(distance[i][j], distance[i][k] + distance[k][j])

  • 时间复杂度:O(n^3),空间复杂度:O(n^2),常数时间小,容易实现
  • 不适用于存在负环的图
int main() {
// n * n 的矩阵
int n = 10;
// 其实就是带权图的邻接矩阵
vector<vector<int>> distance(n, vector<int>(n, INT_MAX)); // 省略 distance 根据给出的边进行初始化 // i 经过 k 到达 j
for (int k = 0; k < n; ++k)
for (int i = 0; i < n; ++i)
for (int j = 0; j < n; ++j)
if (distance[i][k] != INT_MAX
&& distance[k][j] != INT_MAX
&& distance[i][j] > distance[i][k] + distance[k][j])
distance[i][j] = distance[i][k] + distance[k][j];
}

P2910 [USACO08OPEN] Clear And Present Danger S

#include <iostream>
#include <vector>
#include <algorithm>
#include <queue> using namespace std; int main() {
int n, m;
cin >> n >> m;
// 找宝藏的路径
vector<int> path(m);
for (int i = 0; i < m; ++i) {
// 序号从 1 开始
cin >> path[i];
// 序号从 0 开始
path[i]--;
} vector<vector<int>> distance(n, vector<int>(n, 0x7fffffff));
for (int i = 0; i < n; ++i)
for (int j = 0; j < n; ++j)
cin >> distance[i][j]; for (int k = 0; k < n; ++k)
for (int i = 0; i < n; ++i)
for (int j = 0; j < n; ++j)
if (distance[i][k] != 0x7fffffff
&& distance[k][j] != 0x7fffffff
&& distance[i][j] > distance[i][k] + distance[k][j])
distance[i][j] = distance[i][k] + distance[k][j]; int res = 0;
for (int i = 0; i + 1 < m; ++i)
res += distance[path[i]][path[i + 1]];
cout << res;
}

Bellman-Ford 算法

解决可以有负边但不能有负环的图,求单源最短路径的算法。

Bellman-Ford 过程:每一轮考察每条边,每条边都尝试进行松弛操作,那么若干点的 distance 会变小。当某一轮发现不再有松弛操作出现时,算法停止。

Bellman-Ford 算法时间复杂度:假设点的数量为 N,边的数量为 M,每一轮时间复杂度 O(M)。最短路存在的情况下,因为 1 次松弛操作会使 1 个点的最短路的边数 +1。而从源点出发到任何点的最短路最多走过全部的 n 个点,所以松弛的轮数必然 <= n - 1。所以Bellman-Ford算法时间复杂度 O(M*N)

重要推广:判断从某个点出发能不能到达负环。上面已经说了,如果从A出发存在最短路(没有负环),那么松弛的轮数必然 <= n - 1。而如果从A点出发到达一个负环,那么松弛操作显然会无休止地进行下去。所以,如果发现从A点出发,在第n轮时松弛操作依然存在,说明从A点出发能够到达一个负环。可以通过设置一个虚拟源点(与原来所有的点都有连接),判断图是否有负环。

ChatGPT 给出的介绍

Bellman-Ford 算法的关键思想是通过多次松弛操作(relaxation)逐步逼近每个顶点的最短路径,最终在迭代结束时,能够正确确定从源点到所有点的最短路径。要理解为什么每一轮松弛能确定一些点的最短距离,可以从以下几个方面解释:

1. 松弛操作的定义

松弛操作的过程是通过检查从某一顶点到另一顶点之间的边,看看是否可以通过这个顶点找到一条更短的路径。如果可以,那么就更新目标顶点的最短距离。

对于每条边 (u, v) ,如果:dist[v] > dist[u] + weight(u, v)

那么就更新:dist[v] = dist[u] + weight(u, v)

2. 多轮松弛的作用

假设图中有 V 个顶点,最坏情况下,从源点到一个顶点的最短路径可能包含最多 V - 1 条边。因此,Bellman-Ford 算法执行 V - 1 轮松弛操作,确保可以找到源点到任意顶点的最短路径。

每一轮松弛都会检查所有边,并尝试通过更短的路径更新节点的最短距离。对于一条最短路径上恰好有 k 条边的节点,最多需要 k 轮松弛来确定该节点的最短距离。

3. 每轮松弛确定某些点的最短距离

在第 i 轮松弛中,Bellman-Ford 算法会确保从源点到任何节点的最短路径最多经过 i 条边。因此,经过 i 轮松弛后,所有最短路径包含 i 条或更少边的节点的最短距离都会被确定。随着轮数的增加,更多节点的最短路径距离会被确定。

特别是,当执行到第 V - 1 轮时,所有节点的最短距离都会被确定,因为最长的简单路径(不包含环)最多包含 V - 1 条边。

4. 确定最短路径

通过多次松弛操作,Bellman-Ford 算法最终能保证:

  • 如果某个点的最短路径被更新为一个值,那么这个值是正确的,因为此时它已经不能通过任何进一步的松弛获得更短的路径。
  • 所以,每一轮松弛后,至少有一些点的最短距离是最终值,不会再发生变化。

5. 总结

每一轮松弛都能确定一部分节点的最短路径,因为它逐渐考虑到从源点到每个节点经过不同数量边的路径。经过最多 V - 1 轮后,所有点的最短路径都会被确定。因此,每一轮松弛后,某些点的最短路径是最终的且不会再改变。

787. K 站中转内最便宜的航班

#include <iostream>
#include <vector>
#include <algorithm>
#include <queue> using namespace std; class Solution {
public:
// 阉割版 Bellman-Ford
int findCheapestPrice(int n, vector<vector<int>> &flights, int src, int dst, int k) {
vector<int> cur(n, INT_MAX);
cur[src] = 0;
for (int i = 0; i <= k; ++i) {
vector<int> nxt(cur);
for (const auto &edge: flights) {
if (cur[edge[0]] == INT_MAX) continue;
// 从旧表中取数据更新,标准的 Bellman—Ford 是从新表中取
nxt[edge[1]] = min(nxt[edge[1]], cur[edge[0]] + edge[2]);
}
cur = nxt;
}
return cur[dst] == INT_MAX ? -1 : cur[dst];
}
};

P3385 【模板】负环

#include <iostream>
#include <vector>
#include <algorithm>
#include <queue> using namespace std; int n, m, T;
const int MAX_VAL = 0x7fffffff; // 链式前向星
vector<int> head;
vector<int> nxt;
vector<int> to;
vector<int> weight;
int cnt; void initGraph() {
// 点的下标从 1 开始
head.resize(n + 1, 0);
nxt.resize((m << 1) + 1);
to.resize((m << 1) + 1);
weight.resize((m << 1) + 1);
fill(begin(head), end(head), 0);
cnt = 1;
} void addEdge(int u, int v, int w) {
nxt[cnt] = head[u];
to[cnt] = v;
weight[cnt] = w;
head[u] = cnt;
cnt++;
} // Bellman-Ford
// 从 1 到各个点的最短距离
vector<int> distances;
// 存放上一轮松弛中有变动的点
queue<int> q;
// 是否在队列中
vector<bool> enter;
// 记录点的松弛次数
vector<int> updateCnt; void initBellmanFord() {
distances.resize(n + 1, MAX_VAL);
enter.resize(n + 1, false);
updateCnt.resize(n + 1, 0);
fill(begin(distances), end(distances), MAX_VAL);
fill(begin(enter), end(enter), false);
fill(begin(updateCnt), end(updateCnt), 0);
} void clearQueue() {
queue<int> empty;
swap(q, empty);
} // 从顶点 1 出发是否能到达负环
bool hasNegativeCircle() {
distances[1] = 0;
updateCnt[1]++;
q.emplace(1);
enter[1] = true; while (!q.empty()) {
int u = q.front();
q.pop();
enter[u] = false;
for (int ei = head[u]; ei > 0; ei = nxt[ei]) {
int v = to[ei];
int w = weight[ei];
// 没法松弛就跳过
if (distances[v] <= distances[u] + w) continue;
distances[v] = distances[u] + w;
// 在队列就跳过
if (enter[v]) continue;
// 到 v 点的路径被松弛了一次
updateCnt[v]++;
if (updateCnt[v] >= n) return true;
q.emplace(v);
enter[v] = true;
}
}
return false;
} int main() {
cin >> T;
// 每组测试用例
for (int i = 0; i < T; ++i) {
cin >> n >> m;
// 初始化
initGraph();
initBellmanFord();
clearQueue();
// 建图
for (int j = 0, u, v, w; j < m; ++j) {
cin >> u >> v >> w;
if (w >= 0) {
addEdge(u, v, w);
addEdge(v, u, w);
} else {
addEdge(u, v, w);
}
}
cout << (hasNegativeCircle() ? "YES" : "NO") << endl;
}
}

A星、Floyod、Bellman-Ford的更多相关文章

  1. Bellman - Ford 算法解决最短路径问题

    Bellman - Ford 算法: 一:基本算法 对于单源最短路径问题,上一篇文章中介绍了 Dijkstra 算法,但是由于 Dijkstra 算法局限于解决非负权的最短路径问题,对于带负权的图就力 ...

  2. ACM/ICPC 之 最短路径-Bellman Ford范例(POJ1556-POJ2240)

    两道Bellman Ford解最短路的范例,Bellman Ford只是一种最短路的方法,两道都可以用dijkstra, SPFA做. Bellman Ford解法是将每条边遍历一次,遍历一次所有边可 ...

  3. poj1860 bellman—ford队列优化 Currency Exchange

    Currency Exchange Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 22123   Accepted: 799 ...

  4. uva 558 - Wormholes(Bellman Ford判断负环)

    题目链接:558 - Wormholes 题目大意:给出n和m,表示有n个点,然后给出m条边,然后判断给出的有向图中是否存在负环. 解题思路:利用Bellman Ford算法,若进行第n次松弛时,还能 ...

  5. Bellman—Ford算法思想

    ---恢复内容开始--- Bellman—Ford算法能在更普遍的情况下(存在负权边)解决单源点最短路径问题.对于给定的带权(有向或无向)图G=(V,E),其源点为s,加权函数w是边集E的映射.对图G ...

  6. Dijkstra算法与Bellman - Ford算法示例(源自网上大牛的博客)【图论】

    题意:题目大意:有N个点,给出从a点到b点的距离,当然a和b是互相可以抵达的,问从1到n的最短距离 poj2387 Description Bessie is out in the field and ...

  7. POJ 2240 Arbitrage (Bellman Ford判正环)

    Arbitrage Time Limit: 1000MS   Memory Limit: 65536K Total Submissions:27167   Accepted: 11440 Descri ...

  8. poj1860 兑换货币(bellman ford判断正环)

    传送门:点击打开链接 题目大意:一个城市有n种货币,m个货币交换点,你有v的钱,每个交换点只能交换两种货币,(A换B或者B换A),每一次交换都有独特的汇率和手续费,问你存不存在一种换法使原来的钱更多. ...

  9. ACM/ICPC 之 Bellman Ford练习题(ZOJ1791(POJ1613))

    这道题稍复杂一些,需要掌握字符串输入的处理+限制了可以行走的时间. ZOJ1791(POJ1613)-Cave Raider //限制行走时间的最短路 //POJ1613-ZOJ1791 //Time ...

  10. poj3259 bellman——ford Wormholes解绝负权问题

    Wormholes Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 35103   Accepted: 12805 Descr ...

随机推荐

  1. 为python安装扩展模块时报错——error: invalid command 'bdist_wheel'

    具体过程: devil@hp:~/lab$ ./bazel-bin/python/pip_package/build_pip_package /tmp/dmlab_pkg2022年 10月 03日 星 ...

  2. Java中0.2减0.1 结果为什么不是0.1?

    double 表示这种类型的数值精度是 float 类型的两倍(有人称之为双精度数值).绝大部 分应用程序都采用double 类型.在很多情况下,float 类型的精度很难满足需求.实际上,只 有很少 ...

  3. QT中Table Widget树控件的使用

    关于Item Widgets中Table Widget的使用方法!了解在QT程序中插入表格得操作! Table Widget树控件的使用 1.创建数据及其初始化Table控件 // 创建一英雄列表,一 ...

  4. Go 链路追踪入门 Opentelemetry

    前言 Opentelemetry 分布式链路跟踪( Distributed Tracing )的概念最早是由 Google 提出来的,发展至今技术已经比较成熟,也是有一些协议标准可以参考.目前在 Tr ...

  5. PPT或Visio比较舒适的RGB配色参数

    1.187 204 235 2.222 156 83 3.117 156 83 4.64 116 52 5.117 121 74 6.69 137 148 7.182 194 154 8.207 19 ...

  6. esphome esp8266刷写遇到的问题

    问题描述: 在尝试打开串口时出现以下错误信息: Failed to execute 'open' on 'SerialPort': Failed to open serial port. 起因: 莫名 ...

  7. pyinstall通过配置.spec文件引用资源文件

    pyinstall通过配置.spec文件引用资源文件 pyinstall可以自动将所有依赖的.py文件连接起来编译成一个可执行exe文件,但是如果在程序中 使用了外部资源,如图片,或者是其它的配置文件 ...

  8. Kubernetes-10:Ingress-nginx介绍及演示

    Ingress介绍 Ingress是什么? ingress 是除了 hostport  nodeport  clusterIP以及云环境专有的负载均衡器外的访问方式,官方提供了Nginx ingres ...

  9. 使用kamailio进行分机注册及互拨

    操作系统版本:Debian 12.5_x64 kamailio版本:5.8.2 kamailio作为专业的SIP服务器,可承担注册服务器的角色.今天记录下kamailio作为注册服务器,承接分机注册, ...

  10. SQL 求中位值

    题目A median is defined as a number separating the higher half of a data set from the lower half. Quer ...