图论-最短路

 一、不存在负权边-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.rhkb.cn/news/292834.html

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

相关文章

第十五届蓝桥杯第三期模拟赛第十题 ← 上楼梯

【问题描述】 小蓝要上一个楼梯&#xff0c;楼梯共有 n 级台阶&#xff08;即小蓝总共要走 n 级&#xff09;。小蓝每一步可以走 a 级、b 级或 c 级台阶。 请问小蓝总共有多少种方案能正好走到楼梯顶端&#xff1f;【输入格式】 输入的第一行包含一个整数 n 。 第二行包含三个整…

DeviceNET转EtherCat:水处理行业新神器

在现代水处理行业&#xff0c;自动化控制系统的运用日益广泛。特别是上位机通过DeviceNET转EtherCAT网关的技术&#xff0c;以其高速、高效和精准的特点&#xff0c;正逐步成为水处理自动化控制的主流解决方案。我们需要了解什么是上位机、DeviceNET和EtherCAT。上位机通常指的…

[蓝桥杯 2019 省赛 AB] 完全二叉树的权值

# [蓝桥杯 2019 省 AB] 完全二叉树的权值 ## 题目描述 给定一棵包含 $N$ 个节点的完全二叉树&#xff0c;树上每个节点都有一个权值&#xff0c;按从上到下、从左到右的顺序依次是 $A_1,A_2, \cdots A_N$&#xff0c;如下图所示&#xff1a; 现在小明要把相同深度的节点的权值…

【学习】软件科技成果鉴定测试有何作用

软件科技成果鉴定测试是针对软件进行项目申报、科技成果鉴定等相关目的进行的测试。软件测试报告可作为项目申报、科技成果鉴定等工作的依据之一。软件类科技成果鉴定测试从软件文档、功能性、使用技术等方面对软件系统进行符合性测试。其测试结果证明软件的质量是否符合技术合…

魔改一个过游戏保护的CE

csdn审核不通过 网易云课堂有配套的免费视频 int0x3 - 主页 文章都传到github了 Notes/外挂/魔改CE at master MrXiao7/Notes GitHub 为什么要编译自己的CE 在游戏逆向的过程中&#xff0c;很多游戏有保护&#xff0c;我们运行原版CE的时候会被检测到 比如我们开着CE运…

先进封装与传统封装和CU-CU混合键合的应用

凸点间距与先进半导体封装 1965年发明了第一个半导体封装&#xff0c;此后封装技术不断发展。现在&#xff0c;有许多封装技术&#xff0c;从最广泛使用的引线键合到最先进的3DIC。之所以叫先进半导体封装&#xff0c;一种分类方法是看凸点间距的大小。较小的凸点间距意味着更…

axios发送get请求但参数中有数组导致请求路径多出了“[]“的处理办法

一、情况 使用axios发送get请求携带了数组参数时&#xff0c;请求路径中就会多出[]字符&#xff0c;而在后端也会报错 二、解决办法 1、安装qs 当前项目的命令行中安装 npm install qs2、引入qs库(使用qs库来将参数对象转换为字符串) // 全局 import qs from qs Vue.proto…

Mybatis-特殊SQL的执行

1. 模糊查询 在MyBatis中进行模糊查询时&#xff0c;有以下三种常见的实现方式&#xff1a; 1.1. 错误示范 先来个准备操作&#xff0c;并做一个错误示例 根据姓名&#xff0c;模糊查询用户&#xff0c;(x小x) 更新数据表 SQLMapper.java package com.sakurapaid.mybatis3…

HarmonyOS 应用开发之Stage模型启动FA模型PageAbility

本小节介绍Stage模型的两种应用组件如何启动FA模型的PageAbility组件。 UIAbility启动PageAbility UIAbility启动PageAbility和UIAbility启动UIAbility的方式完全相同。 说明&#xff1a; 需注意FA模型中abilityName由bundleName AbilityName组成&#xff0c;具体见示例。 i…

0 决策树基础

目录 1 绪论 2 模型 3 决策树面试总结 1 绪论 决策树算法包括ID3、C4.5以及C5.0等&#xff0c;这些算法容易理解&#xff0c;适用各种数据&#xff0c;在解决各种问题时都有良好表现&#xff0c;尤其是以树模型为核心的各种集成算法&#xff0c;在各个行业和领域都有广泛的…

外包干了5天,技术退步明显.......

先说一下自己的情况&#xff0c;大专生&#xff0c;18年通过校招进入杭州某软件公司&#xff0c;干了接近4年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落! 而我已经在一个企业干了四年的功能测…

react+vite+antD+reduce+echarts项目完整记录

reactviteantDreduceecharts项目完整记录 之前写前端项目&#xff0c;都是用的vue&#xff0c;从最开始的vue2到后来的vue3&#xff0c;断断续续写了3年&#xff0c;打包工具也从webpack转到了vite&#xff0c;全局数据管理工具从vuex转到了pinia。总体而言&#xff0c;vue3对…

【从前端入门到全栈】前端框架之核心概念

大家好&#xff0c;我是江辰&#xff0c;从前端入门到全栈是我全新系列文章&#xff0c;从去年一直囔囔着要写&#xff0c;今年总算开始了&#xff01;预计在10篇左右。知识面从 前端&#xff0c;后端&#xff0c;运维&#xff0c;脚本等&#xff0c;都有涉及&#xff0c;主打一…

C语言预处理详解

前言 上篇博客我们总结了编译与链接&#xff0c;有说过编译里第一步是预处理&#xff0c;那本篇博客将对预处理进行进一步的详细的总结 个人主页&#xff1a;小张同学zkf 若有问题 评论区见 感兴趣就关注一下吧 目录 1. 预定义符号 2. #define 定义常量 3. #define定义宏 4…

实践笔记-harbor搭建(版本:2.9.0)

harbor搭建 1.下载安装包&#xff08;版本&#xff1a;2.9.0&#xff09;2.修改配置文件3.安装4.访问harbor5.可能用得上的命令: 环境&#xff1a;centos7 1.下载安装包&#xff08;版本&#xff1a;2.9.0&#xff09; 网盘资源&#xff1a;https://pan.baidu.com/s/1fcoJIa4x…

睿尔曼超轻量仿人机械臂之复合机器人底盘介绍及接口调用

机器人移动平台是一个包含完整成熟的感知、认知和定位导航能力的轮式机器人底盘产品级平台&#xff0c;产品致力于为各行业细分市场的商用轮式服务机器人提供一站式移动机器人解决方案&#xff0c;让合作伙伴专注在核心业务/人机交互的实现。以下是我司产品双臂机器人以及复合升…

安装部署MariaDB数据库管理系统

目录 一、初始化MariaDB服务 1、安装、启动数据库服务程序、将服务加入开机启动项中。 2、为保证数据库安全性和正常运转&#xff0c;需要对数据库程序进行初始化操作。 3、配置防火墙&#xff0c;放行对数据库服务程序的访问请求&#xff0c;允许管理员root能远程访问数据…

使用 Spring Email 和 Thymeleaf 技术,向新注册用户发送激活邮件(一)

这篇内容对应"2.1 发送邮件"小节 邮箱设置 需要去邮箱对应的官方客户端软件或网站开启IMAP/SMTP服务或POP3/SMTP服务器 如果不开启&#xff0c;就无法使用第三方用户代理&#xff0c;只能走第官方的电子邮件客户端软件或网站&#xff0c;用户代理就是电子邮件客户…

2024-03-26 Android8.1 px30 WI-FI 模块rtl8821cu调试记录

一、kernel 驱动&#xff0c;我这里使用v5.8.1.2_35530.20191025_COEX20191014-4141这个版本&#xff0c;下载这个版本的驱动可以参考下面的文章。 2021-04-12 RK3288 Android7.1 USB wifi bluetooth 模块RTL8821CU 调试记录_rk平台rtl8821cu蓝牙调试-CSDN博客 二、Makefile文…

C++从入门到精通——引用()

C的引用 前言一、C引用概念二、引用特性交换指针引用 三、常引用保证值不变权限的方法权限的放大权限的缩小权限的平移类型转换临时变量 四、引用的使用场景1. 做参数2. 做返回值 五、传值、传引用效率比较值和引用的作为返回值类型的性能比较 六、引用和指针的区别引用和指针的…