最短路(Dijkstra, Bellman-Ford, SPFA, Floyd)

最短路

Dijkstra算法(复杂度 O ( m l o g n ) O(mlog n) O(mlogn)/ O ( n m l o g n ) O(nmlogn) O(nmlogn))–不能有负权边,不能有负权环,单源最短路径( O ( m l o g n ) O(mlog n) O(mlogn)),多源最短路径( O ( n m l o g n ) O(nmlogn) O(nmlogn))。
Bellman-Ford算法(复杂度 O ( n m ) O(nm) O(nm))—能有负权边,不能有负权环,单源最短路径,有边数限制的最短路
SPFA算法(复杂度 O ( k m ) O(km) O(km))—能有负权边,不能有负权环,单源最短路径
Floyd(复杂度 O ( n 3 ) O(n^3) O(n3))—可以求多源最短路,可以处理负权图,不可处理有负权环的图!

Dijkstra

Dijkstra-数据结构:三个数据结构

int dis[10004],vis[10004];
priority_queue<pair<int,int>,vector<pair<int,int>>,greater<pair<int,int>>> q;

//各个数据结构的作用讲解:
dis[]:
vis[]:模拟了一个集合,vis[i]=1表示顶点 i 被加进vis集合中,vis[i]=0表示顶点 i 未被加进集合中。
q:注意优化队列一定要放pair<int,int>,并且前面一定要是距离,后面才是点!被放进去的点是有可能可以用来更新其他点的。

Dijkstra-初始化:

memset(dis,0x3f,sizeof dis); //dis数组应该初始化为无穷 
memset(vis,0,sizeof vis);

Dijkstra-预处理:

dis[i]=0; 
//vis[i]=1; //注意!不能加这一步。 
q.push({0,i}); //<长度,点>

Dijkstra-核心代码:

while(q.size())
{pair<int,int> tmp=q.top();q.pop();int t=tmp.second;if(vis[t]==1) continue;vis[t]=1;for(int i=head[t];i;i=nxt[i]){if(vis[to[i]]==0 && dis[t]+w[i]<dis[to[i]]){dis[to[i]]=dis[t]+w[i];q.push({dis[to[i]],to[i]});}}
}

Dijkstra-最后得到的东西:
一个dis[]数组,记录着起点i到其余各顶点的最短距离。

算法复杂度:
应该是O(nlog n)

好题:
旅行

旅行从城市 1 1 1到城市 n n n, 一天游玩一个城市。点与点之间有一些固定费用的路,但每隔一天需要多付出 w w w的费用测核酸。求最少花费。

#include <bits/stdc++.h>
using namespace std;#define int long long
#define pii pair<int,int>
#define se second
#define fi first
#define pb push_back
const int N=1e5+5;
int n,m,x;
int dis[N][2],vis[N][2];
vector<pii> v[N]; //<点,距离> 
priority_queue<pair<int,pii>,vector<pair<int,pii>>,greater<pair<int,pii>>> q;void Dij()
{memset(dis,0x3f,sizeof dis);memset(vis,0,sizeof vis);dis[1][0]=0;q.push({0,{1,0}}); //<距离,点,标志>while(q.size()){auto tmp=q.top();q.pop();int t=tmp.se.fi;int f=tmp.se.se;if(vis[t][f]==1) continue;vis[t][f]=1;for(int i=0;i<v[t].size();++i) //遍历t点相连的点{if(dis[t][f]+v[t][i].se+(f?0:x)<dis[v[t][i].fi][f^1]){dis[v[t][i].fi][f^1]=dis[t][f]+v[t][i].se+(f?0:x);q.push({dis[v[t][i].fi][f^1],{v[t][i].fi,f^1}});}} }
}signed main(){scanf("%lld%lld%lld",&n,&m,&x); int tt,ttt,w;for(int i=1;i<=m;++i){scanf("%lld%lld%lld",&tt,&ttt,&w); v[tt].pb({ttt,w});v[ttt].pb({tt,w});}Dij();//len[1]=1 printf("%lld\n",min(dis[n][1],dis[n][0]));
}

心得:考虑清楚一个点可以由什么点去松弛得到。

Floyd

参考 算法思想:从第1个到第n个点依次加入松弛计算,每个点加入进行试探枚举是否有路径长度被更改(自己能否更新路径)。顺序加入(k枚举)松弛的点时候,需要遍历图中每一个点对(i,j双重循环),判断每一个点对距离是否因为加入的点而发生最小距离变化,如果发生改变(变小),那么两点(i,j)距离就更改。

应用一:求任意两点间的最短路
应用二:删边,从而降低复杂度 2021CCPC女生赛 C. 连锁商店

//Floyd(弗洛伊得)算法 
特别注意!!!两点之间的距离:
D[i][j]=0  (若i==j) 
D[i][j]=inf  (若i!=j且无边相连)
D[i][j]=W[i][j]  (若i!=j且有边相连)算法描述:
for(k=1;k<=N;k++) //中间点for(i=1;i<=N;i++) //起点 for(j=1;j<=N;j++) //终点 if(D[i][k]+D[k][j]<D[i][j])D[i][j]=D[i][k]+D[k][j]; //更新最小值结束:
D[i][j]就是从i到j的最短路径的长度。 优化:
第一层循环:for(i=1;i<=N/2;i++) ——然后 D[i][j] = D[j][i] = ...如果是固定源点的话,就 去掉第一层循环。 
Bellman-Ford

有边数限制的最短路

题意:有n个点m条边的图(图中可能存在重边和自环, 边权可能为负数,可能存在负权回路),求从1号点到n号点的最多经过k条边的最短距离。如果不存在满足条件的路径,则输出 impossible

参考代码:

#include <bits/stdc++.h>
using namespace std;#define int long long
const int inf=0x3f3f3f3f;
const int N=510,M=1e4+4;
int n,m,k,dis[N],tmp[N];struct edge{int x,y,z;
}e[M];void bellman_ford(){ //答案存在dis里  for(int i=1;i<=n;++i) dis[i]=inf;dis[1]=0;for(int i=0;i<k;++i){ //限制k条边,松弛k次for(int j=1;j<=n;++j) tmp[j]=dis[j];for(int j=1;j<=m;++j){ //遍历m条边int u=e[j].x,v=e[j].y,w=e[j].z;dis[v]=min(dis[v],tmp[u]+w);}}
} signed main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin>>n>>m>>k;for(int i=1;i<=m;++i) cin>>e[i].x>>e[i].y>>e[i].z;bellman_ford(); if(dis[n]>(0x3f3f3f3f/2)) cout<<"impossible\n"; //有可能边是负数 else cout<<dis[n]<<'\n';
}
SPFA

算法步骤

数据结构:
Dis 代表S到I点的当前最短距离。
Fa 代表S到I的当前最短路径中I之前的一个点的编号。
维护一个队列,里面存放所有需要进行迭代的点。初始时队列中只有一个点S。
用一个布尔数组记录每一个点是否在队列中。

初始化:
开始时,Dist全部为inf,只有Dist[S]=0,Fa全部为0 。

过程:

每次迭代,取出队头的点v,依次枚举从v出发的边v->u,设边的长度为len,判断Dist[v]+len是否小于Dis[u],若小于则改进Dist[u],将Fa[u]记为v,并且由于S到u的最短距离变小了,有可能u可以改进其它的点,所以若u不在队列中,就将它放入队尾。这样一直迭代下去,直到队列变空,也就是说S到所有结点的距离都确定下来,结束算法。

算法复杂度

在平均情况下,SPFA算法的期望时间复杂度O(KM) 。M为边数,K是每个点平均入队次数。

但是,有很特殊的情况,这张图是一个棋盘格子,网格图,你的SPFA是很有可能被卡到NM的然后它就死了。所以在OI里面,有一句话,关于SPFA,它死了。

所以没有负权边,就尽量不要用它了,万一被卡了。

对比Bellman-Ford:
Bellman-Ford:每一次松弛的时候Bellman-Ford都要枚举所有的点,而其实很多点都是不需要被枚举的,所以会有很多的无效枚举,使得算法效率降低。
SPFA:其实每次松弛的时候只需要枚举与上次被松弛的点相连的点就可以了。

求最短路(含负权边)
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 510,M = 10010;int n,m,k;
int dist[N],backup[N]; //backup数组为上次
struct edges
{int a,b,w; // a->b权值为w的边 
}edge[M];int bellman_ford()
{memset(dist,0x3f,sizeof(dist));dist[1] = 0;for(int i=0; i<k; i++) {memcpy(backup,dist,sizeof(dist)); //备份for(int j=0; j<m; j++)   // 枚举所有边 {int a = edge[j].a, b = edge[j].b, w=edge[j].w;	dist[b] = min(dist[b],backup[a]+w); // 用备份更新 }}if(dist[n] > 0x3f3f3f3f/2) return -1;return dist[n];
}
int main()
{cin >> n >> m >> k;for(int i=0; i<m; i++){int a,b,w;cin >> a >> b >> w;edge[i] = {a,b,w}; 	}	int t = bellman_ford();if(t == -1) cout << "impossible";else cout << t;return 0;
} 
判断是否存在负权环

若一个点入队次数超过n,则有负权环。因为它最多被n-1个点(其他所有点)更新。所以要看是否存在负权环,用一个cnt[]数组,记录每个点的入队次数。

裸题:洛谷-P3385 【模板】负环

#include <bits/stdc++.h>
using namespace std;const int N = 2e3+10;
const int M = 3e4+10; //开这个数,要细心一点,像无向图,它是对称存储的,如m=2,其实是需要存4条边,当m=2e3时,其实会有4e3条边,如果只开2e3会出现各种错,像re、wa等 
const int inf = 0x3f3f3f3f;
int num;
int head[N];
int to[M];
int w[M];
int ne[M];int T,n,m,dis[N],vis[N],cnt[N];inline void addedge(int x,int y,int z){to[++num]=y;w[num]=z;ne[num]=head[x];head[x]=num;
}void SPFA(){queue<int> q;//初始化memset(dis,0x3f,sizeof dis);dis[1]=0;vis[1]=1;q.push(1);++cnt[1];while(q.size()){int u=q.front();q.pop();vis[u]=0;for(int i=head[u];i;i=ne[i]){if(dis[u]+w[i]<dis[to[i]]){dis[to[i]]=dis[u]+w[i];if(vis[to[i]]==0){q.push(to[i]);vis[to[i]]=1;++cnt[to[i]]; //入队一次就加一次。 if(cnt[to[i]]>=n){cout << "YES" << endl;return;}}}}}cout << "NO" << endl;
} int main(){cin >> T;while(T--){memset(head,0,sizeof head);memset(to,0,sizeof to);memset(ne,0,sizeof ne);memset(w,0,sizeof w);num=0;memset(vis,0,sizeof vis);memset(cnt,0,sizeof cnt);cin >> n >> m;for(int i=1;i<=m;i++){int x,y,z;cin >> x >> y >> z;addedge(x,y,z);if(z>=0)addedge(y,x,z);}SPFA();	}return 0;
}

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

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

相关文章

SQL dialect is not configured. Apache Cassandra matches best.没有配置SQL方言 如何处理

我这里是MySQL语言,所以我设置MySQL dialect 写个记录,之后更换全局SQL语言再换 下图是设置

boot https ssl 使用http协议访问报错

在springboot中配置ssl以后&#xff0c; 再次使用http访问对应的接口就会报错 可以考虑如下设置&#xff0c;将http访问的端口重定向到https对应的端口 import org.apache.catalina.Context; import org.apache.catalina.connector.Connector; import org.apache.tomcat.util…

神经网络中多层卷积的作用

在神经网络中采用多层卷积的目的是为了逐步提取和组合图像的抽象特征&#xff0c;从而更有效地学习数据的表示并执行复杂的任务。不同层的卷积具有不同的作用&#xff0c;从较低层次的特征&#xff08;例如边缘、纹理&#xff09;到较高层次的抽象特征&#xff08;例如物体部件…

ZISUOJ 数据结构--串及其应用

说明&#xff1a; 都是字符串的基本操作没啥好说的&#xff0c;直接上题目和代码了。 题目列表&#xff1a; 问题 A: 字符串翻转 参考题解&#xff1a; #include <iostream> #include <string> #include <algorithm> using std::cin; using std::cout; usi…

OpenHarmony语言基础类库【@ohos.util.PlainArray (非线性容器PlainArray)】

PlainArray可用于存储具有关联关系的key-value键值对集合&#xff0c;存储元素中key值唯一&#xff0c;key值类型为number类型&#xff0c;每个key对应一个value。 PlainArray依据泛型定义&#xff0c;采用轻量级结构&#xff0c;集合中key值的查找依赖于二分查找算法&#xf…

数字电路-可预置倒计时器Multisim仿真

数字电路之于FPGA意义重大。本可预置倒计时器设计采用40106作为振荡电路&#xff0c;由74LSl92、74LS47D和七段共阴数码管构成计时电路&#xff0c;具有启动/预置、暂停/继续计时和报警功能。紫色文字是超链接&#xff0c;点击自动跳转至相关博文。持续更新&#xff0c;原创不易…

用C实现通讯录(详细讲解+源码)

前言 &#x1f4da;作者简介&#xff1a;爱编程的小马&#xff0c;正在学习C/C&#xff0c;Linux及MySQL.. &#x1f4da;以后会将数据结构收录为一个系列&#xff0c;敬请期待 ● 本期内容会给大家带来通讯录的讲解&#xff0c;主要是利用结构体来实现通讯录&#xff0c;该通讯…

[ESP32]:TFLite Micro推理CIFAR10模型

[ESP32]&#xff1a;TFLite Micro推理CIFAR10模型 模型训练 数据集处理 from keras.datasets import cifar10 from keras.preprocessing.image import ImageDataGenerator from keras.models import Sequential, load_model, Model from keras.layers import Input, Dense, …

xLua详解

目录 环境准备xLua导入 C#调用LuaLua解析器Lua文件加载重定向Lua解析管理器全局变量的获取全局函数的获取List和Dictionary映射table类映射table接口映射tableLuaTable映射table Lua调用C#准备工作Lua使用C#类Lua调用C#枚举Lua使用C# 数组 List 字典数组List字典 Lua使用C#扩展…

解决NetworkManager覆盖/etc/resolv.conf的问题

发布时间&#xff1a;2024.4.27 问题 /etc/resolv.conf是Linux下DNS的配置文件。 但是NetworkManager会用覆盖它&#xff0c;导致我们每次都要重新配置。 解决办法 这是官方推荐的做法。或者你可以用resolveconf工具。 $ nm-connection-editor会调起一个界面&#xff0c;…

Python_AI库 matplotlib扩展知识

Python_AI库 matplotlib扩展知识 在数据分析和处理的领域里&#xff0c;可视化是一种不可或缺的手段。通过图形化的展示&#xff0c;我们可以更直观地理解数据的分布、趋势和关系。而matplotlib&#xff0c;作为Python中最为流行的数据可视化库之一&#xff0c;以其强大的功能…

【C++】简易二叉搜索树

目录 一、概念&#xff1a; 二、代码实现&#xff1a; 大致结构&#xff1a; 1、遍历&#xff1a; 2、insert 3、find 4、erase 三、总结&#xff1a; 一、概念&#xff1a; 二叉搜索树又称为二叉排序树&#xff0c;是一种具有特殊性质的二叉树&#xff0c;对于每一个节…

在虚拟环境中找到Qt Designer

Pyqt5中找到Qt Designer 安装Pyqt5和Qt Designer: pip install pyqt5-tools 假设Python的虚拟环境名为:d2l &#xff0c;虚拟环境在d2l文件夹中 D:\Software\d2l\Lib\site-packages\qt5_applications\Qt\bin 双击Qt designer启动 Pyside2中找到Qt Designer d2l是虚拟环境…

上位机图像处理和嵌入式模块部署(树莓派4b下使用sqlite3)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 嵌入式设备下面&#xff0c;有的时候也要对数据进行处理和保存。如果处理的数据不是很多&#xff0c;一般用json就可以。但是数据如果量比较大&…

rust前端web开发框架yew使用

构建完整基于 rust 的 web 应用,使用yew框架 trunk 构建、打包、发布 wasm web 应用 安装后会作为一个系统命令&#xff0c;默认有两个特性开启 rustls - 客户端与服务端通信的 tls 库update_check - 用于应用启动时启动更新检查&#xff0c;应用有更新时提示用户更新。nati…

Linux——终端

一、终端 1、终端是什么 终端最初是指终端设备&#xff08;Terminal&#xff09;&#xff0c;它是一种用户与计算机系统进行交互的硬件设备。在早期的计算机系统中&#xff0c;终端通常是一台带有键盘和显示器的电脑&#xff0c;用户通过它输入命令&#xff0c;计算机在执行命…

SpringBoot引入Layui样式总是出现404

一般出现Layui样式文件如css&#xff0c;js404的错误 解决方案 &#xff08;1&#xff09;首先将其中的静态资源下载resources/static中 &#xff08;2&#xff09;在启动类中重写方法 package com.gq.booksystem;import org.mybatis.spring.annotation.MapperScan; import …

centOS 7.9操作

名称日期版本作者centOS7.9操作2024.4.271.0lll 实验题目&#xff1a; 创建一个用户。 在创建的用户中再创建一个2024的目录。 在2024的下在创建一个 1---10的目录&#xff0c;再创建一个a--z.txt的文件。 在创建一个2024bak的目录。 再将当前用户的所有文件备份到2024ba…

【算法学习】线段树基础版

一 线段树 1.概念 线段树可以理解为一个二叉树&#xff0c;如果是利用线段树求区间的和&#xff0c;那么每个结点的权值维护的是结点所维护区间的和&#xff0c;再将该区间一分为二&#xff0c;分别交由左右儿子维护。 拿区间1 - 4的和来举例子&#xff0c; 根结点维护的是区…

JavaEE——Spring Boot入门

目录 &#x1f4da; JavaEE——Spring Boot入门 &#x1f527; 1. 新建Spring Boot项目 &#x1f6e0; 2. 添加pom依赖 &#x1f4dd; 3. 添加application.yml文件 &#x1f4c2; 4. 创建Dao层 &#x1f527; 5. 创建Service层 &#x1f5a5;️ 6. 创建Controller层及HT…