图论-最短路

 一、不存在负权边-dijkstra算法

dijkstra算法适用于这样一类问题:

从起点 start 到所有其他节点的最短路径。

其实求解最短路径最暴力的方法就是使用bfs广搜一下,但是要一次求得所有点的最短距离我们不可能循环n次,这样复杂度太高,因此dijlstra算法应运而生,算法流程如下:

(待补充)

对于:

稠密图一般使用邻接矩阵+朴素dji

稀疏图使用邻接表+堆优化dji

1.1 朴素djikstra算法

算法模板:

#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;const int N = 510;
int n,m;
int g[N][N];
int dist[N];
bool st[N];
int dijkstra(){memset(st,0,sizeof(st));memset(dist,0x3f,sizeof(dist));dist[1] = 0;//对于每个点都要遍历所有点,也即是穷举for(int i = 0;i < n; i++){int t = -1;//临时存储第i次处理的点//一、遍历所有点,在所有没有访问过的点中找到距离最近的点for(int j = 1; j<= n;j++)if(!st[j] && (t == -1 || dist[t] > dist[j])){t = j;}//st[t] = true;//二、用1-t的路径长度 + t - j的边长度来更新dist[j];for(int j = 1;j <= n;j++){dist[j] = min(dist[j],dist[t] + g[t][j]);}}if(dist[n] == 0x3f3f3f3f)return -1;return dist[n];}

1.2 优先级队列优化的djikstra

首先我们分析,如果是稀疏图,什么导致的朴素版djikstra复杂度高,首先我们用的是邻接表来存图,那么就要用bfs相应的方法进行遍历,那么我们需要先while处理点再内层处理边,如果不优化,复杂度依旧是o(mn)

这是因为我们将所有的顶点都遍历了,用于寻找最小距离点,如果我们使用堆来优化(而不是一般bfs中的普通队列),每次使用优先级队列来存放顶点,因为理想情况下优先级队列中最多装 n 个节点,对优先级队列的操作次数和 m 成正比,所以整体的时间复杂度就是 O(mlogn)

红字解释:

因为在修改其它顶点最短距离的过程中,堆优化版本并没有遍历所有的顶点,而是遍历所有与当前选取的最小顶点有关的边 ,从一小部分顶点出发就能到达所有顶点,因此没有必要遍历所有顶点

优先级队列解释: 

priority_queue<pair<int, int>, vector<pair<int, int>>, greater<>> pq;

pair中 第一个整数可以表示从源点到该顶点的距离,第二个整数表示顶点的编号。

vector<pair<int, int>>指定了底层容器类型。

greater<>:默认小顶堆,less<>:大顶堆排序

#include <iostream>
#include <vector>
#include <queue>
#include <cstring>
using namespace std;const int N = 1e5 + 10;
const int INF = 0x3f3f3f3f;
//只存一个点,因为另一个点是邻接表的表头
struct Edge {int to, weight;
};vector<Edge> adj[N];
int dist[N];
bool visited[N];void dijkstra(int source) {memset(dist, 0x3f, sizeof(dist));memset(visited, 0, sizeof(visited));priority_queue<pair<int, int>, vector<pair<int, int>>, greater<>> pq;dist[source] = 0;pq.push({0, source});while (!pq.empty()) {int u = pq.top().second;pq.pop();if (visited[u]) continue;visited[u] = true;for (auto &e : adj[u]) {int v = e.to;int w = e.weight;if (dist[v] > dist[u] + w) {dist[v] = dist[u] + w;pq.push({dist[v], v});}}}
}int main() {int n, m;cin >> n >> m;for (int i = 0; i < m; ++i) {int x, y, z;cin >> x >> y >> z;adj[x].push_back({y, z});}dijkstra(1); // 假设你想要从节点 1 找到到其他所有节点的最短路径if (dist[n] == INF) cout << "-1\n"; // 检查是否存在从1到n的路径else cout << dist[n] << "\n";return 0;
}

二、存在负权值 -bellma-ford算法

1.朴素Bellman-Ford算法

 三角不等式:

对于所有点都有:

dist[b] <= dist[a] + w

 松弛操作: 

dist[b] = min(dist[b], dist[a] + w)

 注意:如果一个图中存在负权回路,那么可能不存在最短路

#include<cstring>
#include<iostream>
#include<algorithm>using namespace std;
const int N = 510;
const int M = 10010;
int n,m,k;
int dist[N],backup[N];
// 边,a表示出点,b表示入点,w表示边的权重
struct edge{int from;int to;int weight;
}edges[M];void bellman_ford(){memset(dist,0x3f,sizeof dist);dist[1] =0;
// 如果第n次迭代仍然会松弛三角不等式(存在更新),就说明存在一条长度是n+1的最短路径,由抽屉原理,路径中至少存在两个相同的点,说明图中存在负权回路。for(int i = 0;i < k;i++){
// 备份,防止读后写memcpy(backup,dist,sizeof(dist));for(int j = 0;j < m; j++){int from = edges[j].from;int to  = edges[j].to;int weight = edges[j].weight;dist[to] = min(dist[to],backup[from] + weight);}}}int main(){scanf("%d%d%d",&n,&m,&k);for(int i = 0;i < m;i++){int a,b,c;scanf("%d%d%d",&a,&b,&c);edges[i] = {a,b,c};}bellman_ford();if(dist[n] > 0x3f3f3f3f/2){puts("impossible");}else{printf("%d\n",dist[n]);}return 0;
}

除了可以用邻接矩阵和邻接表外,还可用三元组存储图
允许存在负权边,而Dijkstra算法不允许
外循环次数决定最小路径的最大边数
若第n次迭代有修改,根据容斥原理知道,一定存在负权环(整个环的权重和为负数)
实际应用:换乘不超过k次的最短路径(限制路径的边数)
backup用于保存上次迭代的结果,避免“写后读”。Dijkstra算法不存在这种情况
由于存在负权回路(注意不是负权边),因此负权回路有可能把自定义的无穷大0x7f7f7f7f变小,由于最多修改10000×10000=108,10000×10000=108,而0x7f7f7f7f>2×108>2×108,故0x7f7f7f7f / 2依旧是“无穷大”,故可用dist[n] > 0x7f7f7f7f / 2判断是否是无穷大
时间复杂度为O(mn)

2.普通队列优化的bellman-ford算法

#include<cstring>
#include <iostream>
#include <vector>
#include <queue>
#include <cstring>
using namespace std;const int N = 1e5 + 10;
const int INF = 0x3f3f3f3f;
//只存一个点,因为另一个点是邻接表的表头
struct Edge {int to, weight;
};vector<Edge> adj[N];
int dist[N];
bool used[N];
int n, m,k;void spfa(int source) {memset(dist, 0x3f, sizeof(dist));dist[source] = 0;queue<int> pq;pq.push(source);used[source] = true;while (!pq.empty()) {int u = pq.front();pq.pop();used[u] = false;for (auto &e : adj[u]) {int v = e.to;int w = e.weight;if (dist[v] > dist[u] + w) {dist[v] = dist[u] + w;if(!used[v]){pq.push(v);used[v] = true;}}}}
}int main() {cin >> n >> m ;for (int i = 0; i < m; ++i) {int x, y, z;cin >> x >> y >> z;adj[x].push_back({y, z});}spfa(1);if(dist[n] > 0x3f3f3f3f/2){puts("impossible");}else{printf("%d\n",dist[n]);}return 0;
}

时间复杂度:

最好:o(m) 

最差:o(mn)

spfa相较于djikstra,如果题目中不进行针对性限制,一般是会比djikstra更快的

spfa求负环数量:

#include<cstring>
#include <iostream>
#include <vector>
#include <queue>
#include <cstring>
using namespace std;const int N = 1e5 + 10;
const int INF = 0x3f3f3f3f;
//只存一个点,因为另一个点是邻接表的表头
struct Edge {int to, weight;
};vector<Edge> adj[N];
int dist[N],cnt[N];
bool used[N];
int n, m,k;
bool iscir = false;void spfa(int source) {// memset(dist, 0x3f, sizeof(dist));queue<int> pq;for(int i =1;i <= n;i++){// dist[i] = 0;pq.push(i);used[i] = true;}while (!pq.empty()) {int u = pq.front();pq.pop();used[u] = false;for (auto &e : adj[u]) {int v = e.to;int w = e.weight;if (dist[v] > dist[u] + w) {dist[v] = dist[u] + w;//判断是否存在负权值cnt[v] = cnt[u] + 1;if(cnt[v] >= n) {iscir = true;return;}if(!used[v]){pq.push(v);used[v] = true;}}}}
}int main() {cin >> n >> m ;for (int i = 0; i < m; ++i) {int x, y, z;cin >> x >> y >> z;adj[x].push_back({y, z});}spfa(1);if(iscir){printf("Yes");}else{printf("No");}return 0;
}

三、Floyd算法

const int INF = 1E9;
// 初始化:for (int i = 1; i <= n; i ++ )for (int j = 1; j <= n; j ++ )if (i == j) d[i][j] = 0;else d[i][j] = INF;// 算法结束后,d[a][b]表示a到b的最短距离
void floyd()
{for (int k = 1; k <= n; k ++ )for (int i = 1; i <= n; i ++ )for (int j = 1; j <= n; j ++ )d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
}

最短距离需要把d[i][i] = 0;
时间复杂度为O(n3) 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/783977.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

推挽输出与开漏输出

推挽输出与开漏输出 文章目录 推挽输出与开漏输出前言一、推挽输出二、开漏输出总结 前言 在使用GPIO口时&#xff0c;会遇到两种配置&#xff0c;一种叫推挽输出&#xff0c;一种叫开漏输出&#xff0c;今天就简聊一聊这两种模式的差异和选择。 一、推挽输出 如图所示&#…

力扣 1035. 不相交的线

题目来源&#xff1a;https://leetcode.cn/problems/uncrossed-lines/description/ C题解&#xff1a;经过细细一推导&#xff0c;就发现跟力扣 1143. 最长公共子序列-CSDN博客 换汤不换药。 直线不能相交&#xff0c;说明元素顺序不能改变&#xff0c;求可以绘制的最大连线数…

【数据结构与算法】二叉树遍历、判断和 diff 算法

遍历 深度优先遍历 function Node(value) {this.value valuethis.left nullthis.right null }let a new Node(a) let b new Node(b) let c new Node(c) let d new Node(d) let e new Node(e) let f new Node(f) let g new Node(g) a.left c a.right b c.l…

动态规划-最长回文子串

动态规划-最长回文子串 原题描述解答中心移动思想代码实现复杂度分析时间复杂度空间复杂度 动态规划思想代码实现复杂度分析时间复杂度空间复杂度 突然觉得很有必要将学过的内容记录下来&#xff0c;这样后续在需要用到的时候就可以避免从头进行学习&#xff0c;而去看自己之前…

鸿蒙OS开发实例:【ArkTS类库多线程I/O密集型任务开发】

使用异步并发可以解决单次I/O任务阻塞的问题&#xff0c;但是如果遇到I/O密集型任务&#xff0c;同样会阻塞线程中其它任务的执行&#xff0c;这时需要使用多线程并发能力来进行解决。 I/O密集型任务的性能重点通常不在于CPU的处理能力&#xff0c;而在于I/O操作的速度和效率。…

ESP8266 WiFi物联网智能插座—上位机软件实现

1、软件架构 上位机主要作为下位机数据上传服务端以及节点调试的控制端&#xff0c;可以等效认为是专属版本调试工具。针对智能插座协议&#xff0c;对于下位机进行可视化监测和管理。 软件技术架构如下&#xff0c;主要为针对 Windows 的PC 端应用程序&#xff0c;采用WPF以及…

HCIA复习

OSI --开放式系统互联参考模型 --- 7层参考模型 TCP/IP协议栈道 --- 4层或5层 OSI&#xff1a; 应用层 抽象语言 -->编码 表示层 编码-->二进制 表示层以下都是二进制-----data&#xff08;数据&#xff09; 会话层 提供应用程序的会话地址 上三层为应用…

七、函数的使用方法

函数的调用 nameinput&#xff08;&#xff09;#输入参数并赋值name print&#xff08;name&#xff09;#d打印name 格式&#xff1a;返回值函数名&#xff08;参数&#xff09; def get_sum(n):#形式参数计算累加和:param n::return: sumsum0for i in range(1,n1):sumiprint…

echarts 图表/SVG 图片指定位置截取

echarts 图表/SVG 图片指定位置截取 1.前期准备2.图片截取3.关于drawImage参数 需求&#xff1a;如下图所示&#xff0c;需要固定头部legend信息 1.前期准备 echarts dom渲染容器 <div :id"barchart id" class"charts" ref"barchart">&…

深入探索位图技术:原理及应用

文章目录 一、引言二、位图&#xff08;Bitset&#xff09;基础知识1、位图的概念2、位图的表示3、位图操作 三、位图的应用场景1、数据查找与存储2、数据去重与排序 四、位图的实现 一、引言 位图&#xff0c;以其高效、简洁的特性在数据处理、存储和检索等多个领域发挥着举足…

Mybatis常见面试题

1&#xff1a;Mybatis执行流程 回答&#xff1a; 读取Mybatis配置文件&#xff1a;mybatis-config.xml加载运行环境和映射文件构造会话工厂SqlSessionFactory会话工厂创建SqlSession对象&#xff08;包含了执行SQL语句的所有方法&#xff09;操作数据库的接口&#xff0c;Exec…

简单的链接中心软件yal

什么是 yal &#xff1f; yal(Yet Another Landingpage) 是一个简单的链接中心&#xff0c;用于显示和搜索链接。允许轻松打造品牌&#xff0c;以最少的权限运行并且易于使用。可以设置吉祥物和徽标&#xff08;目前是强制性的&#xff09;。 软件特点 静态生成的站点单个静态…

python通过shapely 的 valid 判断aoi图形是否有效

测试aoi坐标&#xff1a; 116.527712,39.924304;116.527123,39.924353;116.52707,39.923985;116.527685,39.92397;116.527712,39.924304 如图所示是一个有效的坐标&#xff0c;使用python代码判断是否有效&#xff1a; 代码&#xff1a; from shapely.geometry import Polyg…

CAJViewer7.3 下载地址及安装教程

CAJViewer是中国学术期刊&#xff08;CAJ&#xff09;全文数据库的专用阅读软件。CAJViewer是中国知识资源总库&#xff08;CNKI&#xff09;开发的一款软件&#xff0c;旨在方便用户在线阅读和下载CAJ数据库中的学术论文、期刊和会议论文等文献资源。 CAJViewer具有直观的界面…

2024年腾讯云服务器99元一年_老用户优惠续费不涨价

腾讯云99元一年服务器配置为轻量2核2G4M、50GB SSD盘、300GB月流量、4M带宽&#xff0c;新用户和老用户都可以购买&#xff0c;续费不涨价&#xff0c;续费价格也是99元一年。以往腾讯云优惠服务器都是新用户专享的&#xff0c;这款99元服务器老用户也可以购买&#xff0c;这是…

Spring Boot 使用 Redis

1&#xff0c;Spring 是如何集成Redis的&#xff1f; 首先我们要使用jar包 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency><gro…

ROS中IMU惯性测量单元

一、IMU惯性测量单元消息包 IMU 是安装在机器人内部的一种传感器模块&#xff0c;用于测量机器人的空间姿态。 IMU的消息包定义在sensor_msgs包中的Imu中。头部是header&#xff0c;记录了消息发送的时间戳和坐标系ID。第二个是角速度。第三个是矢量加速度。三个数据成员都各…

从姿态估计到3D动画

在本文中&#xff0c;我们将尝试通过跟踪 2D 视频中的动作来渲染人物的 3D 动画。 在 3D 图形中制作人物动画需要大量的运动跟踪器来跟踪人物的动作&#xff0c;并且还需要时间手动制作每个肢体的动画。 我们的目标是提供一种节省时间的方法来完成同样的任务。 我们对这个问题…

C++数据类型(一):一文看懂引用的本质

一.引言 函数的参数传递方式主要有传值和传指针。 1.传值 在函数域中为参数分配内存&#xff0c;而把实参的数值传递到新分配的内存中。它的优点是有效避免函数的副作用。 例如&#xff1a; #include <iostream>void swap_val(int x,int y) {int tmp;tmp x;x y;y …

P6学习:Oracle Primavera P6 OBS/责任人解析

前言 Primavera P6 EPPM 责任人用于管理 P6 企业项目组合管理 (EPPM) 系统中的项目所有权和权限。 Primavera P6 EPPM 中的所有项目都至少围绕三个结构进行组织&#xff1a;称为企业项目结构 (EPS) 的用于组织项目的结构、称为工作分解结构 (WBS) 的用于组织项目内活动的结构…