当前位置: 首页 > news >正文

Day5课件

最短路

一个联通图中有n个点,m条边。边有权值(可正可负),可有向可无向。给定起点s和终点t,在所以连接s到t的路径中寻找边的权值之和最小的路径,这就是最短路径问题。

单源最短路

给定图,求一个点到所有其它点的最短路径。

image-20260121111338086

Dijkstra算法:

该算法要求边权不为负,核心策略是每次选择当前距离起点最近的未确定点,用它来更新其他点的距离。

1.设置两个顶点的集合T和S:
S中存放已找到最短路径的顶点,初始状态时,集合S中只有一个顶点,即源点v0;
T中存放当前还未找到最短路径的顶点;
2.以后每求得一条最短路径(v0,…,vk),就将vk加入到顶点集合S中,直到所有的顶点都加入到集合S中,算法就结束了

算法的具体步骤
1.初始时,S中包含源点v0。
2.然后不断从T中选取到顶点v0路径长度最短的顶点u,找到后:
a)把顶点u加入到S;
b)修改顶点v0到T中剩余顶点的最短路径长度值。T中各顶点新的最短路径长度值为:min{原来的最短路径长度值, 顶点u的最短路径长度+u到该顶点的路径长度值};
3.重复2,直到T的顶点全部加入S为止。

初始化起点距离为0,其他点为无穷大,每次取出距离最小的点,用它更新邻居,重复直到剩下的顶点不可达。

vector<int> dijkstra(int n, int start, vector<vector<pair<int, int>>>& graph) {vector<int> dist(n + 1, INT_MAX);vector<bool> visited(n + 1, false);dist[start] = 0;for (int i = 1; i <= n; i++) {// 寻找未访问的最小距离顶点int u = -1;for (int j = 1; j <= n; j++) {if (!visited[j] && (u == -1 || dist[j] < dist[u])) {u = j;}}if (dist[u] == INT_MAX) break; // 剩下的顶点不可达visited[u] = true;// 用u更新相邻顶点for (auto& edge : graph[u]) {int v = edge.first;int w = edge.second;if (dist[v] > dist[u] + w) {dist[v] = dist[u] + w;}}}return dist;
}

注意到上面在每次寻找未访问的最小距离节点时会遍历所有节点,这会浪费一些时间,可以使用优先队列进行优化。

vector<int> dijkstraHeap(int n, int start, vector<vector<pair<int, int>>>& graph) {vector<int> dist(n + 1, INT_MAX);dist[start] = 0;// 使用优先队列(最小堆)// pair<距离, 顶点>priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;pq.push({0, start});while (!pq.empty()) {auto [d, u] = pq.top();pq.pop();// 如果当前距离不是最短的,跳过if (d > dist[u]) continue;for (auto& edge : graph[u]) {int v = edge.first;int w = edge.second;if (dist[v] > dist[u] + w) {dist[v] = dist[u] + w;pq.push({dist[v], v});}}}return dist;
}

朴素版本:O(V²) ,堆优化版本:O((V+E)logV)

优点:效率高,适合稠密图和稀疏图

缺点:不能处理负权边

练习题

动态演示

证明

Bellman-Ford算法:

该算法可处理负边权,但不能有负环

负环

image-20260121112434799

先介绍 Bellman–Ford 算法要用到的松弛操作(Dijkstra 算法也会用到松弛操作).

对于边(u,v),松弛操作对应下面的式子:dis(v) = min(dis(v), dis(u) + w(u, v))

这么做的含义是显然的:我们尝试用 𝑆 →𝑢 →𝑣,其中 𝑆 →𝑢的路径取最短路)这条路径去更新 𝑣点最短路的长度,如果这条路径更优,就进行更新.

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

每次循环是 𝑂(𝑚)
O(m) 的,那么最多会循环多少次呢?

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

但还有一种情况,如果从 𝑆 点出发,抵达一个负环时,松弛操作会无休止地进行下去.注意到前面的论证中已经说明了,对于最短路存在的图,松弛操作最多只会执行n-1 轮,因此如果第n轮循环时仍然存在能松弛的边,说明从S 点出发,能够抵达一个负环.


struct Edge {int u, v, w;
};vector<Edge> edge;int dis[MAXN], u, v, w;
constexpr int INF = 0x3f3f3f3f;bool bellmanford(int n, int s) {memset(dis, 0x3f, (n + 1) * sizeof(int));dis[s] = 0;bool flag = false;  // 判断一轮循环过程中是否发生松弛操作for (int i = 1; i <= n; i++) {flag = false;for (int j = 0; j < edge.size(); j++) {u = edge[j].u, v = edge[j].v, w = edge[j].w;if (dis[u] == INF) continue;// 无穷大与常数加减仍然为无穷大// 因此最短路长度为 INF 的点引出的边不可能发生松弛操作if (dis[v] > dis[u] + w) {dis[v] = dis[u] + w;flag = true;}}// 没有可以松弛的边时就停止算法if (!flag) {break;}}// 第 n 轮循环仍然可以松弛时说明 s 点可以抵达一个负环return flag;
}

时间复杂度:O(VE),V为顶点数,E为边数

优点:可以处理负权边,能检测负环

缺点:效率较低,不适合稠密图

练习题

记录路径

我们引入path数组用以记录当前点在最短路径中的前驱节点,每次更新某个点的最短路径值时同时更新其path值。

在Dijkstra中,我们修改更新临近节点的部分为

for (auto& edge : graph[u]) {int v = edge.first;int w = edge.second;if (dist[v] > dist[u] + w) {dist[v] = dist[u] + w;path[v] = u;}
}

同样,在Bellman-Ford中,可将松弛操作更改为

for (int j = 0; j < edge.size(); j++) {u = edge[j].u, v = edge[j].v, w = edge[j].w;if (dis[u] == INF) continue;if (dis[v] > dis[u] + w) {dis[v] = dis[u] + w;path[v] = u;flag = true;}
}

在记录path数组后,可通过如下操作输出路径

void run(int s, int t) {     //s为起点,t为终点if(s == t) cout << s << " ";else {run(s, path[t]);cout << t << " ";}
}

练习题

简单计数

对于边权值为一的的图,最短路的值就是路径上边的个数。可以从起点开始进行bfs,由于每条边权相同,我们第一次访问某个点时就是最短路,我们从起点开始,向队列中加入距离起点为1的点,距离起点为2的点只能从距离为1的点过来,而我们在从队列中取出距离为2的点进行遍历之前我们已经处理完距离为1的点,此时我们对到距离为2的点的路径已经计数完成,依此类推即可。

vector<int> d(n + 1, 1e18), cn(n + 1, 0);  //d数组记录路径长度,cn数组计数
queue<int> q;
q.push(1);
while (!q.empty()) {auto u = q.front();q.pop();for (auto v : e[u]) {if (d[v] == 1e18) {      //第一次访问q.push(v);d[v] = d[u] + 1;cn[v] = cn[u];} else if (d[v] == d[u] + 1) {    // 后续访问cn[v] += cn[u];}}
}

练习题

多源最短路

Floyd 算法:

是用来求任意两个结点之间的最短路的.复杂度比较高,但是常数小,容易实现(只有三个 for).适用于任何图,不管有向无向,边权正负,但是最短路必须存在.(不能有个负环)。

我们定义一个数组 f[k][x][y],表示只允许经过结点 1到k(也就是说,在子图 𝑉′ =1,2,…,𝑘V'={1, 2, \ldots, k} 中的路径,注意,𝑥x 与 𝑦y 不一定在这个子图中),结点 x到结点y的最短路长度.

很显然,f[n][x][y] 就是结点x到结点y的最短路长度(因为 𝑉′ =1,2,…,𝑛V'={1, 2, \ldots, n} 即为 𝑉V 本身,其表示的最短路径就是所求路径).

接下来考虑如何求出 f 数组的值.

f[0][x][y]:𝑥x 与 𝑦y 的边权,或者 00,或者正无穷(f[0][x][y] 什么时候应该是 +∞+\infty?当 𝑥x 与 𝑦y 间有直接相连的边的时候,为它们的边权;当x=y的时候为零,因为到本身的距离为零;当x与y没有直接相连的边的时候,为正无穷+\infty).

f[k][x][y] = min(f[k-1][x][y], f[k-1][x][k]+f[k-1][k][y])f[k-1][x][y],为不经过 𝑘k 点的最短路径,而 f[k-1][x][k]+f[k-1][k][y],为经过了k点的最短路).

上面两行都显然是对的,所以说这个做法空间是 𝑂(𝑁3)O(N^3),我们需要依次增加问题规模(k从1到n),判断任意两点在当前问题规模下的最短路.

因为第一维对结果无影响,我们可以发现数组的第一维是可以省略的,于是可以直接改成 f[x][y] = min(f[x][y], f[x][k]+f[k][y])

vector<vector<int>> floydWarshall(int n, vector<vector<pair<int, int>>>& graph) {// 初始化距离矩阵vector<vector<int>> dist(n + 1, vector<int>(n + 1, INT_MAX));// 初始化:自己到自己的距离为0,有边的直接赋值for (int i = 1; i <= n; i++) {dist[i][i] = 0;for (auto& edge : graph[i]) {int v = edge.first;int w = edge.second;dist[i][v] = min(dist[i][v], w); // 处理重边}}// Floyd核心算法for (int k = 1; k <= n; k++) {for (int i = 1; i <= n; i++) {for (int j = 1; j <= n; j++) {if (dist[i][k] != INT_MAX && dist[k][j] != INT_MAX) {dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);}}}}return dist;
}

时间复杂度:O(V³),空间复杂度:O(V²)

优点:代码简单,可以处理负权边(但不能有负环)

缺点:时间复杂度高,只适合小规模图(V≤500)

练习题

http://www.jsqmd.com/news/302806/

相关文章:

  • 工业互联网新场景:将实时碳排数据集成到现有能源管理系统的三种方案
  • AI辅助的开题报告模板,助你轻松搞定学术写作任务
  • 【项目开源】一个基于Spring Cloud Alibaba的充电桩运营管理后台
  • 基于STM32F103VET6外部中断的矩阵键盘高精度计算器实现
  • 存储服务器大流量写入由于 Ring Buffer 设置不合理导致丢包、断流的处理
  • 【日记】或许我只是接受不了要求(2543 字)
  • 冰雪聚贤,智启新局——2026崇礼论坛凝聚AI长期主义共识
  • A2UI 技术原理深度解析:AI Agent 如何安全生成富交互 UI
  • A2UI vs 传统模式:AI Agent UI 生成方案对比与 Token 消耗分析
  • 量子计算机的实用性为何依赖经典计算
  • 2026灵活用工新趋势:技术人才如何抓住“碎片化”就业红利?
  • 【源码可参考】开源能源数据监控平台:使用Spring Boot + Vue + 时序数据库开发实践
  • 基于非对称算法的资料下载安全方案设计
  • CMOS版图分析
  • 分析全国专业的少儿专注力培训公司,天使英才费用贵吗
  • 盘点专业的少儿大脑潜能开发品牌企业,排名情况如何
  • AI加持的开题报告模板,助你快速完成高质量学术写作
  • 想要加速学术写作?AI定制的开题报告模板不容错过
  • 这份AI优化的开题报告模板,让你的写作更高效精准
  • 这份AI增强的开题报告模板,是学术写作的理想选择
  • 降低AIGC生成内容重复率的最佳网站排名:10大免费与付费平台方案详细对比
  • 从0开始学语音检测:FSMN-VAD新手实战教程
  • 2026年汽车座椅发泡生产线设备厂商性价比排名,选购要点分享
  • 深度测评降低AIGC率的优质网站:10个平台免费版与付费版差异对比
  • 广州有实力的洗发水代加工定制厂家哪家性价比高
  • 借助AI工具,这份开题报告模板能显著提升学术写作效率。
  • 深聊输送链条行业,口碑好的品牌和专业制造厂盘点
  • 解读售后完善的少儿专注力培训公司,北京地区哪家更靠谱
  • 高效学术写作?试试这份AI优化的开题报告模板
  • AI驱动的开题报告模板,让你的学术写作事半功倍