最短路(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.mfbz.cn/a/580275.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

《苍穹外卖》Day10部分知识点记录

一、Spring Task 介绍 Spring Task是Spring框架提供的任务调度工具&#xff0c;可以按照约定的时间自动执行某个代码逻辑。 定位&#xff1a;定时任务框架 作用&#xff1a;定时自动执行某段Java代码 应用场景&#xff1a;只要是需要定时处理的场景都可以使用Spring Task …

飞书API(6):使用 pandas 处理数据并写入 MySQL 数据库

一、引入 上一篇了解了飞书 28 种数据类型通过接口读取到的数据结构&#xff0c;本文开始探讨如何将这些数据写入 MySQL 数据库。这个工作流的起点是从 API 获取到的一个完整的数据&#xff0c;终点是写入 MySQL 数据表&#xff0c;表结构和维格表结构类似。在过程中可以有不同…

重生奇迹mu装备掉落大全

1、骷髅兵&#xff1a; [一般宝]毒戒指(3%HP)石巨人召唤石玛雅雷之项链(1%)。 2、独眼巨人&#xff1a;4冰之戒指(2%)3雷之项链(2%)3毒之戒指天使3毒戒(3%回复)灵魂祝福石巨人石玛雅钻云枪石。 3、幽灵&#xff1a;3雷链(hp3%)守护天使小恶魔&#xff0c;灵魂宝石祝福4冰戒回3…

AI赋能分层模式,解构未来,智领风潮

​&#x1f308; 个人主页&#xff1a;danci_ &#x1f525; 系列专栏&#xff1a;《设计模式》 &#x1f4aa;&#x1f3fb; 制定明确可量化的目标&#xff0c;坚持默默的做事。 &#x1f680; 转载自热榜文章&#x1f525;&#xff1a;探索设计模式的魅力&#xff1a;AI赋能分…

【探索Java编程:从入门到入狱】Day3

&#x1f36c; 博主介绍&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 hacker-routing &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【应急响应】 【Java、PHP】 【VulnHub靶场复现】【面试分析】 &#x1f389;点赞➕评论➕收…

Redis分布式锁 - 基于Jedis和LUA的分布式锁

先基于单机模式&#xff0c;基于Jedis手工造轮子实现自己的分布式锁。 首先看两个命令&#xff1a; Redis 分布式锁机制&#xff0c;主要借助 setnx 和 expire 两个命令完成。 setnx命令: setnx 是 set if not exists 的简写。将 key 的值设为 value &#xff0c;当且仅当…

跨设备自动化协同提效新利器!边缘自动化流程编排工具

痛点剖析 随着企业生产环境的日益复杂化&#xff0c;不同生产设备间的协调性问题尤为凸显。 1、不同设备往往基于各自的技术标准、通信协议和操作系统设计&#xff0c;这使得它们之间的数据交换和指令传递存在显著的障碍。 2、技术上的不兼容性导致设备间难以实现无缝对接和…

Matplotlib是什么?

一、Matplotlib是什么&#xff1f; Matplotlib是一个Python语言的2D绘图库&#xff0c;它非常广泛地用于数据的可视化。以下是一些主要特点&#xff1a; 多功能性&#xff1a;它允许用户创建各种静态、动态或交互式的图表&#xff0c;如线图、散点图、直方图等。跨平台性&…

基于MSP430F249的电子钟仿真(源码+仿真)

目录 1、前言 2、仿真 3、程序 资料下载地址&#xff1a;基于MSP430F249的电子钟仿真(源码仿真&#xff09; 1、前言 基于MSP430F249的电子钟仿真&#xff0c;数码管显示时分秒&#xff0c;并可以通过按键调节时间。 2、仿真 3、程序 #include <MSP430x24x.h> #def…

Spring Boot项目中的ASCII艺术字

佛祖保佑&#xff1a; ${spring-boot.formatted-version} ———————————————————————————————————————————————————————————————————— // _ooOoo_ …

tomcat系统架构及运用

文章目录 下面是Tomcat架构的详细解析&#xff1a;1. **Server&#xff08;服务器&#xff09;**2. **Service&#xff08;服务&#xff09;**3. **Container&#xff08;容器&#xff09;** - 分层结构4. **Connectors&#xff08;连接器&#xff09;**5. **类加载器&#xff…

数据集笔记:处理北大POI 数据:保留北京POI

数据来源&#xff1a;Map POI (Point of Interest) data - Official data of the contest (pku.edu.cn) windows 下载方法&#xff1a;数据集笔记&#xff1a;windows系统下载北大开放数据研究平台的POI数据-CSDN博客 1 读取数据 1.1 列出所有的文件 dir1D:/data/PKU POI/2…

如何管理约束

本文主要介绍如何管理约束&#xff0c;包括决定何时发生约束检查&#xff0c;如何删除约束&#xff0c;删除和更新父行&#xff0c;插入和更新子行。 1. 约束事务模式 约束事务模式决定何时发生引用违例检查。 对于具有日志记录的数据库 — 即时约束&#xff08;Immediate con…

【笔试强训】Day4 --- Fibonacci数列 + 单词搜索 + 杨辉三角

文章目录 1. Fibonacci数列2. 单词搜索3. 杨辉三角 1. Fibonacci数列 【链接】&#xff1a;Fibonacci数列 解题思路&#xff1a;简单模拟题&#xff0c;要最少的步数就是找离N最近的Fibonacci数&#xff0c;即可能情况只有比他小的最大的那个Fibonacci数以及比他大的最小的那…

【VUE】Vue中实现树状表格结构编辑与版本对比的详细技术实现

Vue中实现树状表格结构编辑与版本对比的详细技术实现 在Vue中&#xff0c;创建一个可编辑的树状表格并实施版本对比功能是一种需求较为常见的场景。在本教程中&#xff0c;我们将使用Vue结合Element UI的el-table组件&#xff0c;来构建一个树状表格&#xff0c;其中包含添加、…

ICCV 2021 | FcaNet: Frequency Channel Attention Networks 中的频率分析

ICCV 2021 | FcaNet: Frequency Channel Attention Networks 中的频率分析 论文&#xff1a;https://arxiv.org/abs/2012.11879代码&#xff1a;https://github.com/cfzd/FcaNet 文章是围绕 2D 的 DCT 进行展开的&#xff0c;本文针对具体的计算逻辑进行梳理和解析。 f ( u ,…

【MySQL精炼宝库】数据库的约束 | 表的设计 | 聚合查询 | 联合查询

目录 一、数据库约束 1.1 约束类型&#xff1a; 1.2 案例演示&#xff1a; 二、表的设计 2.1 一对一: 2.2 一对多: 2.3 多对多: 2.4 内容小结&#xff1a; 三、新增 四、查询 4.1 聚合查询&#xff1a; 4.1.1 聚合函数&#xff1a; 4.1.2 GROUP BY子句&#xff1a…

linux使用docker 安装mysql redis

linux安装docker https://hub-stage.docker.com/ 前往这里搜索容器来部署。每个容器都有独立的运行环境。 具体安装教程 https://docs.docker.com/engine/install/centos/#install-using-the-repository 检查是否安装成功&#xff1a; sudo docker --version 配置国内镜像加速…

人脸识别概念解析

目录 1. 概述 2. 人脸检测 3. 人脸跟踪 4. 质量评价 5. 活体检测 6. 特征提取 7. 人脸验证 8. 人脸辨识 1. 概述 人脸识别在我们的生活中随处可见&#xff0c;例如在大楼门禁系统中&#xff0c;它取代了传统的门禁卡或密码&#xff0c;提高了进出的便捷性和安全性。在商…

Adfind的使用

Adfind是一个使用C语言写的活动目录查询工具&#xff0c;它允许用户轻松地搜索各种活动目录信息。它不需要安装&#xff0c;因为它是基于命令行的。它提供了许多选项&#xff0c;可以细化搜索并返回相关细节。下面讲解Adfind的参数以及其使用。 参数 执行如下命令即可查看Adf…
最新文章