第三章 搜索与图论(二)(最短路)

一、最短路问题

1、对于稠密图,由于朴素版的dijkstra算法与边数无关使用这种算法的复杂度较低。稀疏图用堆优化版的算法;单源最短路中存在负权边用SPFA 算法通常较好;多源用floyd算法;

 难点:如何建图,抽象为最短路问题。

二、朴素版dijkstra算法

由于稠密图用这种算法,邻接矩阵存图,注意把g初始化为0x3f;st保存每个数组的状态,

#include<bits/stdc++.h>
//849dijkstra最短路
using namespace std;
const int N=510;
int g[N][N],disk[N],st[N];
int n,m;
int dijkstra()
{disk[1]=0;for(int i=1;i<=n;i++){int t=-1;//找最小的那个for(int j=1;j<=n;j++)if(st[j]==-1&&(disk[t]>disk[j]||t==-1))t=j;st[t]=1;//标记for(int j=1;j<=n;j++)disk[j]=min(disk[j],disk[t]+g[t][j]);}if(disk[n]==0x3f3f3f3f)return -1;return disk[n];
}
int main()
{cin>>n>>m;memset(disk,0x3f,sizeof disk);//初始化最短路为正无穷memset(g,0x3f,sizeof g);memset(st,-1,sizeof st);for(int i=0;i<m;i++){int a,b,c;cin>>a>>b>>c;//由于存在重复带权边,所以要留住那个较短的边因此初始化g为无穷g[a][b]=min(g[a][b],c);}int t=dijkstra();cout<<t<<endl;return 0;
}

三、堆优化版的dijkstra算法

在获取disk最小的节点的位置进行优化。用堆来存储起点到目标节点的距离。又因为算法每次更新边,更新一次堆的时间复杂度为log(n),时间复杂为O(m log(n))

直接使用优先队列实现堆,优先队列中的元素为pair元素,first为路径长度,second为端点名字。

#include<bits/stdc++.h>
//850dijkstra最短路(堆优化)邻接表存图
using namespace std;
const int N=1e5+10;
typedef pair<int,int> PII;
int h[N],e[N],ne[N],idx,w[N];
int disk[N],st[N];
int n,m;
void add(int a,int b,int c)//将边和权重保存到邻接表中
{e[idx]=b;ne[idx]=h[a];w[idx]=c,h[a]=idx++;
}
int dijkstra()
{disk[1]=0;priority_queue<PII,vector<PII>,greater<PII>>heap;//小顶堆heap.push({0,1});while(heap.size()){auto t=heap.top();//拿出堆顶heap.pop();int ver=t.second,distance=t.first;if(st[ver]==1) continue;//已经确定过最短路继续st[ver]=1;for(int i=h[ver];i!=-1;i=ne[i])//遍历与其相连的所有边{int j=e[i];if(disk[j]>distance+w[i]){//更新后的disk可能不是最小值//这些没有用的中间值即使跑到堆顶取出来了//但是只要发现对应的点已经确定最短路了就放弃disk[j]=distance+w[i];heap.push({disk[j],j});}}}if(disk[n]==0x3f3f3f3f)return -1;return disk[n];
}
int main()
{cin>>n>>m;memset(disk,0x3f,sizeof disk);//初始化最短路为正无穷memset(st,-1,sizeof st);memset(h,-1,sizeof h);for(int i=0;i<m;i++){int a,b,c;cin>>a>>b>>c;add(a,b,c);}int t=dijkstra();cout<<t<<endl;return 0;
}

三、  Bellman_Ford算法(用于求有边数限制的最短路)

可以用于用来找负环,或者有最多经过k条边的限制的题目。

而且即使有负环让你求最短路,有k条边的限制也不会出现负无穷的结果

算法:进行指定次数的循环,每次循环都遍历所有边

#include<bits/stdc++.h>
// 853. 有边数限制的最短路 (bellman_ford)
using namespace std;
const int N=510,M=10010;
int n,m,k;
int dist[N],backup[N];
struct Edege//结构体保存边,便于遍历
{int a,b,c;
}edges[M];int bellman_ford()
{memset(dist,0x3f,sizeof dist);//初始化dist[1]=0;for(int i=0;i<k;i++){memcpy(backup,dist,sizeof dist);//复制上一次的结果到backup,//防止发生串性地更新for(int i=0;i<m;i++){int a=edges[i].a,b=edges[i].b,w=edges[i].c;dist[b]=min(dist[b],backup[a]+w);}}//由于存在负边,可能会小于0x3f3f3f3f,根据题目的数据范围,计算最多减的次数if(dist[n]>=0x3f3f3f3f/2)return -1;else return dist[n];
}
int main()
{cin>>n>>m>>k;for(int i=0;i<m;i++){int a,b,w;cin>>a>>b>>w;edges[i]={a,b,w};//保存边}int t=bellman_ford();if(t==-1)puts("impossible");else cout<<t<<endl;return 0;}

四、SPFA算法求最短路

SPFA算法其实就是在Bellman-ford算法基础上的优化。Bellman-ford算法看起来比较傻,每次迭代的话是遍历所有边来更新,但是每次迭代的话不是每条边都会更新。SPFA算法就是对这个做优化,每次迭代dist[b]可以更新的话,一定是dist[a]变小了,因为如果dist[a]不变的话,dist[b]一定不变。只有dist[a]变小了,它的后继才会变小。所以SPFA算法就从这个点进行优化。

SPFA算法的思路就是迭代的时候用一个队列来做,队列里面存的就是到起点距离变小的点。先把起点放到队列里面去,只要队列不空,也就是队列里面还有距离变小的点的话,就执行一下操作:

先取出队头t,然后队头出队
更新t的所有出边,t到起点的距离不是变小了吗, 那么所有和t相连的点都有可能变小,如果更新成功的话,就入队。但是注意要判断一下这个点已经入过队的话就不用重复加入了。

————————————————           
原文链接:https://blog.csdn.net/qq_52905520/article/details/126574584

例题:利用spfa算法求最短路,从队列头拿出结点,只要能更新就更新,并把更新的点加入到队列中,并利用st bool数组保存队列中是否已经有当前结点。

spfa算法中不使用backup的原因是每次更新只更新邻边,不会出现串联更新。

#include<bits/stdc++.h>
//
using namespace std;
const int N=1e5+10;
int h[N],e[N],ne[N],idx,w[N];
int dist[N];
bool st[N];
int n,m;
void add(int a,int b,int c)
{e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;}
int spfa()
{memset(dist,0x3f,sizeof dist);memset(st,false,sizeof st);dist[1]=0;queue<int>q;//队列存所有更新过的点dist[1]=0;q.push(1);//入队while(q.size()){int t=q.front();//拿出一个元素q.pop();st[t]=false;for(int i=h[t];i!=-1;i=ne[i]){int j=e[i];//首先更新if(dist[j]>dist[t]+w[i]){dist[j]=dist[t]+w[i];//先更新if(!st[j])//只有队列里面没有的时候才能入队{q.push(j);st[j]=true;}}}}if(dist[n]==0x3f3f3f3f)return -1;return dist[n];
}
int main()
{cin>>n>>m;memset(h,-1,sizeof h);while(m--){int a,b,c;cin>>a>>b>>c;add(a,b,c);}int t=spfa();if(t==-1)puts("impossible");else cout<<t<<endl;return 0;
}

五、spfa算法判断是否有负环

通过最短路的边数来判断是否有负环 。

#include<bits/stdc++.h>
//852 spfa判断是否
using namespace std;
const int N=1e5+10;
int h[N],e[N],ne[N],idx,w[N];
int dist[N];
bool st[N];
int cnt[N];//记录边数
int n,m;
void add(int a,int b,int c)
{e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;}
int spfa()
{memset(dist,0x3f,sizeof dist);memset(st,false,sizeof st);dist[1]=0;queue<int>q;//队列存所有更新过的点//因为有可能负环从第一个结点到不了所以把所有结点放到队列for(int i=1;i<=n;i++)q.push(i),st[i]=true;dist[1]=0;while(q.size()){int t=q.front();//拿出一个元素q.pop();st[t]=false;for(int i=h[t];i!=-1;i=ne[i]){int j=e[i];//首先更新if(dist[j]>dist[t]+w[i]){dist[j]=dist[t]+w[i];//先更新cnt[j]=cnt[t]+1;if(cnt[j]>n)return 1;if(!st[j])//只有队列里面没有的时候才能入队{q.push(j);st[j]=true;}}}}return 0;
}
int main()
{cin>>n>>m;memset(h,-1,sizeof h);while(m--){int a,b,c;cin>>a>>b>>c;add(a,b,c);}int t=spfa();if(spfa())puts("Yes");else puts("No");
}

六、弗洛伊德Floyd算法求多源最短路

三重循环

#include<bits/stdc++.h>
//852 spfa判断是否
using namespace std;
const int N=210,INF=1e9;
int d[N][N];
int n,m,Q;
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]);
}
int main()
{cin>>n>>m>>Q;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;}}while(m--){int a,b,c;cin>>a>>b>>c;d[a][b]=min(d[a][b],c);}floyd();while(Q--){int a,b;cin>>a>>b;if(d[a][b]<INF/2) cout<<d[a][b]<<endl;else cout<<"imposible"<<endl;}}

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

spring boot学习第十二篇:mybatis框架中调用存储过程控制事务性

1、MySQL方面&#xff0c;已经准备好了存储过程&#xff0c;参考&#xff1a;MYSQL存储过程&#xff08;含入参、出参&#xff09;-CSDN博客 2、pom.xml文件内容如下&#xff1a; <?xml version"1.0" encoding"UTF-8"?> <project xmlns"…

【Web】vulhub Shiro-550反序列化漏洞复现学习笔记

目录 Shiro简介 复现流程 工具一把梭 半脚本半手动 原理分析 反序列化入口 常见的key 登录过程 验证过程 利用原理 Shiro简介 Apache Shiro 是一个强大且易于使用的 Java 安全框架&#xff0c;用于身份验证、授权、加密和会话管理等安全功能。Shiro 的设计目标是简单…

猫头虎分享已解决Bug || Python Error: NameError: name ‘variable_name‘ is not defined

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

6个好看的wordpress模板

简站wordpress服务业通用主题 2023年立秋纪念版&#xff0c;简站wordpress服务行业通用主题&#xff0c;适合服务行业企业官网使用。 https://www.jianzhanpress.com/?p5393 小语种翻译wordpress主题 小语种国家外贸网站建设需要的wordpress主题模板&#xff0c;适合做小语…

Pinia介绍和使用

1. pinia是什么 Pinia 是一个基于 Vue.js 的状态管理库&#xff0c;用于管理应用程序的数据。它提供了一种简单、直观且可扩展的方式来组织和访问应用程序的状态&#xff0c;下面是详细介绍 基于 Vue 3&#xff1a;Pinia 是专门为 Vue 3 开发的状态管理库&#xff0c;充分利用…

【若依】若依框架在本地运行的操作方法,及踩坑记录

若依框架简介 若依是一个Gitee上一个开源的基于SpringBoot开发的轻量级Java快速开发框架&#xff0c;用以快速构建后台管理系统&#xff0c;点击跳转到官方地址 本机部署过程 Step1. 下载项目源码 我选择的是直接下载zip压缩包&#xff0c;解压后得到如下文件夹&#xff0c…

QLabel重绘实现圆角矩形图片/文本和图片同时显示

QLabel一般用于显示一段文字&#xff0c;这段文字可以被鼠标选中/复制&#xff0c;也可是设置自动换行等&#xff0c;还可以用于显示图片。 但是使用QLabel显示图片时&#xff0c;qss样式设置的圆角radius属性是不生效的。 QLabel显示纯文本时&#xff0c;设置了背景颜色后&a…

WifiConfigStore初始化读取-Android13

WifiConfigStore初始化读取 1、StoreData创建并注册2、WifiConfigStore读取2.1 文件读取流程2.2 时序图2.3 日志 1、StoreData创建并注册 packages/modules/Wifi/service/java/com/android/server/wifi/WifiConfigManager.java mWifiConfigStore.registerStoreData(mNetworkL…

pytorch入门第一天

今天作为入门pytorch的第一天。打算记录每天学习pytorch的一些理解和笔记&#xff0c;以用来后面回顾。当然如果能帮到和我一样的初学者&#xff0c;那也是不胜荣幸。作为一名初学者&#xff0c;难免有些地方会现错误&#xff0c;欢迎各位大佬指出 预备知识 这里主要介绍pyto…

面向对象的三大特征之一继承

继承 继承的特性 概念&#xff1a;可以使得子类具有父类的属性(成员变量)和方法(成员方法)&#xff0c;还可以在子类中重新定义&#xff0c;追加属性和方法。 继承的格式&#xff1a; public class 子类名 extends 父类名{} 父类&#xff1a;基类、超类 子类&#xff1a;派生…

React + SpringBoot + Minio实现文件的预览

思路&#xff1a;后端提供接口&#xff0c;从minio获取文件的预览链接&#xff0c;返回给前端&#xff0c;前端使用组件进行渲染展示 这里我从minio获取文件预览地址用到了一个最近刚开源的项目&#xff0c;挺好用的&#xff0c;大伙可以试试&#xff0c;用法也很简单 官网&am…

C语言笔试题之实现C库函数 pow()(递归的思想)

实例要求&#xff1a; 1、请你实现C库函数 pow()&#xff08;stdio.h & math.h&#xff09; &#xff0c;即计算 x 的整数 n 次幂函数&#xff08;即x^n &#xff09;&#xff1b;2、函数声明&#xff1a;double myPow(double x, int n)&#xff1b;参数&#xff1a;1、x …

C++进阶(十三)异常

&#x1f4d8;北尘_&#xff1a;个人主页 &#x1f30e;个人专栏:《Linux操作系统》《经典算法试题 》《C》 《数据结构与算法》 ☀️走在路上&#xff0c;不忘来时的初心 文章目录 一、C语言传统的处理错误的方式二、C异常概念三、异常的使用1、异常的抛出和捕获2、异常的重新…

leetcode707. 设计链表

leetcode707. 设计链表 题目 思路 1.使用虚头节点&#xff0c;模拟class的初始化 2.class中添加一个链表长度的属性&#xff0c;便于后续操作 代码 class ListNode:def __init__(self, val0, nextNone):self.val valself.next nextclass MyLinkedList:def __init__(self)…

创新指南|生成式AI实验 - 企业快速渐进采用人工智能的科学新方法

生成式人工智能&#xff08;Gen AI&#xff09;正迅速成为各行各业的企业创新焦点。 生成式AI实验对于企业创新而言至关重要&#xff0c;不仅可以帮助企业识别最适合和最有影响的应用场景&#xff0c;还能促进组织沿着生成式 AI 学习曲线前进&#xff0c;建立早期的创新领导者和…

Elementplus报错 [ElOnlyChild] no valid child node found

报错描述&#xff1a;ElementPlusError: [ElOnlyChild] no valid child node found 问题复现&#xff08;随机例子&#xff09;&#xff1a; <el-popover placement"right" :width"400" trigger"click"><template #reference><e…

零基础学Python之Unitest模块

1.unittest简介及入门案例 &#xff08;1&#xff09;什么是Unitest Unittest是Python自带的单元测试框架&#xff0c;不仅适用于单元测试&#xff0c;还可用于Web、Appium、接口自动化测试用例的开发与执行。该测试框架可组织执行测试用例&#xff0c;并且提供丰富的断言方法…

Unity引擎学习笔记之【动画层操作】

动画层Animation Layer 一、动画器的三个基本状态 1. Any State&#xff08;任意状态&#xff09; “Any State”&#xff08;任意状态&#xff09;&#xff1a;这个状态可以用来连接多个状态机的任意状态转换。在动画控制器中&#xff0c;你可以使用“Any State”作为过渡条…

问题:银行账号建立以后,一般需要维护哪些设置,不包括() #学习方法#经验分享

问题&#xff1a;银行账号建立以后&#xff0c;一般需要维护哪些设置&#xff0c;不包括&#xff08;&#xff09; A&#xff0e;维护结算科目对照 B&#xff0e;期初余额初始化刷 C&#xff0e;自定义转账定义 D&#xff0e;对账单初始化 参考答案如图所示

c入门第十篇——指针入门

一句话来说: 指针就是存储了内存地址值的变量。 在前面讨论传值和传址的时候&#xff0c;我们就已经开始使用了指针来传递地址。 在正式介绍指针之前&#xff0c;我们先来简单了解一下内存。内存可以简单的理解为一排连续的房子的街道&#xff0c;每个房子都有自己的地址&#…