C++图网结构算法

news2025/3/13 19:11:01

目录

一.迪杰斯特拉算法(dijkstra)

1.实现原理:

2.代码实现:

3.例题:

二.spfa算法:

1.实现原理:

2.代码实现:

3.例题:

三.贝尔曼福特(bellman_ford)

1.实现原理:

2.代码实现:

3.例题:

四.弗洛伊德(floyd)

1.实现原理:

2.代码实现:

3.例题:

4.floyd性质探索:

(1).性质一

(2).性质二

(3).性质三

5.floyd传递闭包问题


一.迪杰斯特拉算法(dijkstra)

1.实现原理:

dijkstra算法能实现的原因是它保证了在第一次出队之后,答案一定为最短。也就是出队之后

,一定不会再有更短的路径出队,而dijkstra算法的实现就是基于边权不为负的情况下。

实现步骤:
1.初始化所有点到起点1的距离为正无穷,且dis[1]=0。
2.将起点信息(0,1)入队进行bfs拓展。
3.将起点相连点入队,即(1,2),(2,3)入队。更新点2,3的距离信息。
4.继续拓展节点2,3,将(9,4),(5,4)以及(9,5)入队。此时队首为(5,4),将该节点出队,得到dis[4]=5。
5.继续拓展,直到终点出队...

2.代码实现:

用dis数组记录到每个点的具体距离,每一次循环后,加上相应的边权

priority_queue<node> q;
void dijkstra(int x) {
	memset(dis,0x3f,sizeof dis);
	dis[x]=0;
	q.push({0,x});
	while(!q.empty()) {
		node t=q.top();
		q.pop();
		int y=t.p;
		vis[y]=true;
		for(int i=he[y]; i; i=nxt[i]) {
			int z=per[i],w=ed[i];
			if(vis[z]==false && dis[z]>dis[y]+w) {
				dis[z]=dis[y]+w;
				q.push({dis[z],z});
			}
		}
	}
}

3.例题:

求单源最短路

给定一个n个点m条边的有向图,图中可能存在重边和自环,所有边权均为正值(边权小于10000)。
请你求出1号点到n号点的最短距离。如果无法从1号点走到n号点,则输出-1。第一行包含整数n和m(n<=1e5,m<=2e5)。
接下来m行每行包含三个整数x,y,z,表示存在一条从点x到点y的有向边,边长为z。

输出一个整数,表示1号点到n号点的最短距离
如果路径不存在,则输出-1。

用例输入 1 

正确代码:

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,m,tot,ed[N*2],nxt[N*2],he[N],per[N*2],dis[N*2];
bool vis[N*2];
void add(int x,int y,int z) {//邻接表
	++tot;
	per[tot]=y,ed[tot]=z,nxt[tot]=he[x],he[x]=tot;
}
struct node {
	int d,p;
};
bool operator < (node x,node y) {//重载运算符,明确在优先队列中的排序依据
	return x.d>y.d;
}
priority_queue<node> q;
void dijkstra(int x) {
	memset(dis,0x3f,sizeof dis);
	dis[x]=0;
	q.push({0,x});
	while(!q.empty()) {
		node t=q.top();
		q.pop();
		int y=t.p;
		vis[y]=true;
		for(int i=he[y]; i; i=nxt[i]) {
			int z=per[i],w=ed[i];
			if(vis[z]==false && dis[z]>dis[y]+w) {//比较最小
				dis[z]=dis[y]+w;
				q.push({dis[z],z});//入队
			}
		}
	}
}
int main() {
	cin>>n>>m;
	for(int i=1; i<=m; i++) {
		int x,y,z;
		cin>>x>>y>>z;
		add(x,y,z);//有向图
	}
	dijkstra(1);
	if(dis[n]==0x3f3f3f3f) cout<<-1;//是否遍历到过,没有就没法从医到这个点
	else cout<<dis[n];
	return 0;
}

二.spfa算法:

对于全是正边权的图,我们可以使用dijkstra处理最短路问题。但是如果带有负边边,dijkstra算法无法保证节点出队时一定得到最短路,spfa算法则可以解决这个问题。

1.实现原理:

在求解各点到起点的最小距离dis值时,若某点i产生一个更小的dis[i],那么节点i后续指向的节点都会重新更新,因此我们可以将该点i再次入队,重新更新。

在枚举到3->5的时候由于它小于之前入队的数,所以要进行出对,并进行更新后再次入队。

2.代码实现:

在dijlstra算法的基础上,加入重新出对与再入队的代码

void spfa() {
	queue<int>q;
	q.push(1);
	memset(dis,0x3f,sizeof dis);
	dis[1]=0;
	vis[1]=true;
	while(!q.empty()) {
		int x=q.front();q.pop();
		vis[x]=false;//出队之后将原来的标记清除
		for(int i=he[x]; i; i=nxt[i]) {
			int y=per[i],w=ed[i];
			if(dis[x]+w<dis[y]){
				dis[y]=dis[x]+w;
				if(vis[y]==false){
					q.push(y);
					vis[y]=true;
				}
			}
		}
	}
}

3.例题:

带负权的最短路计算

给定一个n个点m条边(n<=1e5,m<=2e5)的有向图,图中可能存在重边和自环,边权绝对值小于104。数据保证图中不存在负权回路。

请你求出1号点到n号点的最短距离。如果无法从1号点走到n号点,则输出-1。

第一行包含整数n和m(n<=1e5,m<=2e5)。

接下来m行每行包含三个整数x,y,z,表示存在一条从点x到点y的有向边,边长为z。

输出一个整数,表示1号点到n号点的最短距离
如果路径不存在,则输出-1。

正确代码:

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n,m,he[N],per[N],nxt[N],tot,dis[N],ed[N];
bool vis[N];
void add(int x,int y,int z) {
	++tot;
	per[tot]=y,ed[tot]=z,nxt[tot]=he[x],he[x]=tot;
}
long long read(){
	int x=0,f=1;
	char c=getchar();
	while(c<'0'||c>'9'){
		if(c=='-') f=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9'){
		x=x*10+c-'0';
		c=getchar();
	}
	return x*f;
}
void spfa() {
	queue<int>q;
	q.push(1);
	memset(dis,0x3f,sizeof dis);
	dis[1]=0;
	vis[1]=true;
	while(!q.empty()) {
		int x=q.front();q.pop();
		vis[x]=false;
		for(int i=he[x]; i; i=nxt[i]) {
			int y=per[i],w=ed[i];
			if(dis[x]+w<dis[y]){
				dis[y]=dis[x]+w;
				if(vis[y]==false){
					q.push(y);
					vis[y]=true;
				}
			}
		}
	}
}
int main() {
	n=read(),m=read();
	for(int i=1; i<=m; i++) {
		int x,y,z;
		x=read(),y=read(),z=read();
		add(x,y,z);
	}
	spfa();
	if(dis[n]==0x3f3f3f3f) cout<<-1;
	else cout<<dis[n];
	return 0;
}

三.贝尔曼福特(bellman_ford)

上面讲到的两种最短路算法(dijkstra与spfa),他们都是由枚举已知顶点更新未知点。如果我们要在最短路上增加边数限定,即不超过k条边所能构成的最短路,我们就需要将边编号,通过枚举边来实现。

1.实现原理:

枚举边,若左端点值已知,则更新右端点的值。初始化所有点的dis值为正无穷,且dis[1]=0。第1次枚举边:得到dis[2]=1,dis[3]=8第2次枚举边:得到dis[4]=1第3次枚举边:得到dis[5]=2
已知:任意一条路径都是由若干条边组成的链。第一次枚举所有边,可以求出与起点直连的点的最短路。第二次枚举所有边,可以求出不超过2条边所构成的最短路。第k次枚举所有边,可以求出不超过k条边所构成的最短路。

2.代码实现:

使用bellman_ford枚举边,所有用结构体存边并且进行编号。

void bellman_ford(){
	memset(dis,0x3f,sizeof dis);
	dis[1]=0;
	for(int i=0;i<k;i++){
		memcpy(dis_old,dis,sizeof dis_old);
		for(int i=1;i<=m;i++) dis[a[i].y]=min(dis[a[i].y],dis_old[a[i].x]+a[i].w);
	}
}

3.例题:

给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环, 边权可能为负数。

请你求出从 1 号点到 n 号点的最多经过 k 条边的最短距离,如果无法从 1 号点走到 n 号点,输出 impossible。

第一行包含三个整数 n,m,k。

接下来 m 行,每行包含三个整数 x,y,z,表示存在一条从点 x
到点 y 的有向边,边长为 z。

点的编号为 1∼n。

输出一个整数,表示从 1 号点到 n 号点的最多经过 k 条边的最短距离。

如果不存在满足条件的路径,则输出 impossible。

正确代码:

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
bool vis[N];
vector<int> v[N];
int n,m,k,dis[N],dis_old[N];//用dis_old记录上一个·点,以此来实现标记边
long long read(){
	int x=0,f=1;
	char c=getchar();
	while(c<'0'||c>'9'){
		if(c=='-') f=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9'){
		x=x*10+c-'0';
		c=getchar();
	}
	return x*f;
}
struct node{
	int x,y,w;
}a[N];
void bellman_ford(){
	memset(dis,0x3f,sizeof dis);
	dis[1]=0;
	for(int i=0;i<k;i++){
		memcpy(dis_old,dis,sizeof dis_old);
		for(int i=1;i<=m;i++) dis[a[i].y]=min(dis[a[i].y],dis_old[a[i].x]+a[i].w);
	}
}
int main() {
	n=read(),m=read(),k=read();
	for(int i=1;i<=m;i++) a[i].x=read(),a[i].y=read(),a[i].w=read();
	bellman_ford();
	if(dis[n]>0x3f3f3f3f) cout<<"impossible";
	else cout<<dis[n];
	return 0;
}

四.弗洛伊德(floyd)

floyd算法专门针对多源最短路问题。floyd可以用一个二维矩阵表示两点之间的最短路径,即我们只需要维护图中最小距离的邻接矩阵。floyd本质上就是通过不断为两点的路径上增加中间点(所以又称为插点法),进而对邻接矩阵进行更新迭代,每加入一个点,矩阵信息就可能变化一次,从而维护该最小距离矩阵。

1.实现原理:

每次枚举是加入一个中间点k,矩阵中有些点对的距离就有可能减小。同时也说明两点的最短路径链上就可能会增加该中间点k。当所有点都作为中间点加入且完成矩阵的更新后,最终矩阵一定存储任意两点之间的距离最小值。

在这幅图中,如果不进行插点,它的初始矩阵是这样的:

1234
100x3f3f3f3f0x3f3f3f3f3
2100x3f3f3f3f8
32503
40x3f3f3f3f0x3f3f3f3f0x3f3f3f3f

0

而在插入1为中间点后,它的矩阵就变成了这样:

1234
100x3f3f3f3f0x3f3f3f3f3
2100x3f3f3f3f4
32503
40x3f3f3f3f0x3f3f3f3f0x3f3f3f3f

0

在插入2为中间点后,它的矩阵又变成这样:

1234
100x3f3f3f3f0x3f3f3f3f3
2100x3f3f3f3f4
32503
40x3f3f3f3f0x3f3f3f3f0x3f3f3f3f

0

最后插入三为中间点后,它的矩阵是这样的:

1234
100x3f3f3f3f0x3f3f3f3f3
2100x3f3f3f3f4
32503
40x3f3f3f3f0x3f3f3f3f0x3f3f3f3f

0

(这只是一个特殊样例,并不是所有可能都只会在插入第一个中间点后才会改变,但经过n-1次插点后路径一定最短)

2.代码实现:

图中存在负边权,且无负权环。n较小,m较大特别稠密,可以使用邻接矩阵存边。最后涉及到多次询问,使用floyd可以维护邻接矩阵,快速求解。

void floyd(){
	for(int k=1;k<=n;k++){
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				if(k==i||k==j||i==j) continue;//可有可无
				if(g[i][j]>g[i][k]+g[k][j]) g[i][j]=g[i][k]+g[k][j],pat[i][j]=k;//修改最短路径
			}
		}
	}
}

3.例题:

给定由n个点m条边的构成无向图,边权可能为负,图中可能存在重边,不存自环以及负权环。

现在给定q组询问,请输出每组询问中节点i到j的最短距离及其路径。

第一行 n,m,q,其中n≤500,m≤106,q≤104。
接下来m行表示这两个点之间有边相连,边权绝对值小于10000。
随后q行,代表接下来有q个询问,每个询问由ai​,bi​构成。

一共输出q行,如果两点不存在路径则输出-1,否则每行第一个数字表示两点之间的最短距离,随后输出其路径(如果存在多条最短路径,则输出字典序最小的一个)

正确代码:

#include<bits/stdc++.h>
using namespace std;
int n,m,q,g[505][505],pat[505][505];
long long read() {
	int x=0,f=1;
	char c=getchar();
	while(c<'0'||c>'9') {
		if(c=='-') f=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9') {
		x=x*10+c-'0';
		c=getchar();
	}
	return x*f;
}
void floyd(){
	for(int k=1;k<=n;k++){
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				if(k==i||k==j||i==j) continue;
				if(g[i][j]>g[i][k]+g[k][j]) g[i][j]=g[i][k]+g[k][j],pat[i][j]=k;
			}
		}
	}
}
void print(int x,int y){
	if(pat[x][y]==0){
		cout<<" "<<y;
		return;
	}
	print(x,pat[x][y]),print(pat[x][y],y);
}
int main(){
	n=read(),m=read(),q=read();
	memset(g,0x3f,sizeof g);
	for(int i=1;i<=m;i++){
		int x,y,w;
		x=read(),y=read(),w=read();
		g[x][y]=g[y][x]=min(g[x][y],w);
	}
	floyd();
	for(int i=1;i<=q;i++){
		int x,y;
		x=read(),y=read();
		if(g[x][y]==-1) cout<<-1<<endl;
		else{
			cout<<g[x][y]<<" "<<x;
			print(x,y);
			cout<<endl;
		}
	}
	return 0;
}

4.floyd性质探索:

(1).性质一

插入中间点的顺序对结果没有影响。

插入一个中间点k后,此时矩阵中任意两点的距离就可能因为该中间点变小,即点k加入了其中两点的路径中。因此,我们只需要保证点1-n都作为中间点插入一次,从而更新矩阵,对插入顺序不作要求。

(2).性质二

floyd算法在第执行k次插点后,最短路径上的边数一定不会超过k+1。

第1次插点后:
两端点由(i->j)直连变成可能加入一个中间点k相连,变成(i->k->j),即最短路径上存在不超过两条边。

第2次插点后:
两端点最短路径有可能还是只有一条边构成(i->j),当然也有可能在第1次插点的基础上增加为两条边构成(i->k->j),此时插点有可能再增加一个点p,变成3条边。即(i->p->k->j) 或 (i->k->p->j)。

第n次插点后:
两端点最短路径上最多由n+2个点构成,即存在不超过n+1条边

(3).性质三

在执行第k次插点操作前,所求任意两端点的最短路径上,一定不包含k以及k后的若干点。

与性质1类似插入某一个点,则说明把该点放到两端点的路径上,从而尝试更新两端点的最小距离。同理,未插入的点是没有更新任意两端点的距离。必不在现有的路径上。

5.floyd传递闭包问题

传递闭包是离散数学中的一个概念,简单来说就是维护关系的传递性。如a<b,b<c则有a<c。
我们以前学习过使用并查集解决部分具有关系传递性的问题。在这里,我们可以通过floyd算法去建立与维护关系矩阵。从而快速判断任意两点是否具备当前关系。可以通过g数组来记录每一个点之间的关系,若g[a][b]=1就代表a>b,若g[a][b]=0,则a<b。如果a、b的关系需要通过k来传递,我们可以通过g[a][b] |= (g[a][k] & g[k][b])来转移他们的关系。

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

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

相关文章

HTTP模块(二)

HTTP 设置 HTTP 响应报文 HTTP报文常见属性&#xff1a; const http require(http);const server http.createServer((request, response) > {// 设置请求状态码 2xx 4xx 5xxresponse.statusCode 200;// 设置请求描述 了解即可response.statusMessage hello// 指定响…

Nodepad++运行Python文件的方法

windows中使用nodepad运行python文件 首先&#xff0c;需要在windows中安装python解释器。 然后打开nodepad&#xff0c;新建一个文件&#xff0c;输入一段测试的python代码 import socketdef get_hostname():try:# 获取主机名hostname socket.gethostname()return hostna…

uniapp集成安卓原生录屏插件以及使用

概述 我们知道UniApp的出现简化了开发者的工作流程&#xff0c;并减少了代码的重复编写。开发者可以使用一套代码编译到iOS、Android、以及各种小程序的应用&#xff0c;节省了人力和时间成本&#xff0c;但是涉及到与系统交互的时候&#xff0c;比如录屏、录音、录像、文件操…

Go语言常见序列化协议全面对比

先说结论 从易用性、性能、内存占用、编码后大小等几个方面综合考虑 ProtoBuf 胜出。 Gob 从性能和 I/O 带宽占用上都和 ProtoBuf 差不多&#xff0c;唯一劣势是编解码时内存占用较多。考虑到不用再写 IDL 带来的易用性&#xff0c;如果整个系统内不存在使用除 Go 以外其他语言…

『Django』搭建你的博客网站

theme: smartblue 点赞 关注 收藏 学会了 本文简介 如果你学了我前面写的 Django 文章&#xff0c;现在已经有能力去试试自己搭建博客网站了。 虽然用的不是现在流行的前后端分离的方式(前后端分离的方式会在之后的文章讲解)。 但在搭建网站之前我们还要做一些额外的功能让你…

【机器学习】梯度下降的基本概念和如何使用梯度下降自动化优化w和b

引言 梯度下降是一种用于寻找函数最小值的优化算法&#xff0c;它在机器学习中广泛用于训练模型&#xff0c;如线性回归、神经网络等 一、梯度下降的基本概念 1.1 目标函数 在机器学习中&#xff0c;这通常是损失函数&#xff08;如均方误差、交叉熵等&#xff09;&#xff0…

[渗透测试] 主动信息收集

主动信息收集 在红蓝对抗过程中&#xff0c;资产属于核心地位&#xff0c;攻击方&#xff08;红方&#xff09;要尽可能的去获取对方资产&#xff0c;暴露目标资产&#xff0c;包括IP地址、网络设备、安全设备、服务器、存储在服务器中的数据等。防守方也要清楚自己有多少有价…

了解网络是如何运作

“Web 的工作原理”提供了一个简化的视图,用于了解在计算机或手机上的 Web 浏览器中查看网页时发生的情况。 这个理论对于短期内编写 Web 代码来说并不是必需的,但不久之后,你就会真正开始从理解后台发生的事情中受益。 客户端和服务器 连接到 Internet 的计算机称为客户端和…

dns逆向解析,主从服务,多域名访问(穿插ntp服务器)

复习 域名解析&#xff1a; 正向解析&#xff1a;将域名解析为ip 反向解析&#xff1a;将ip解析为域名 逆向解析 关闭防火墙和selinux&#xff0c;配置静态ip [rootdns ~]# vim /etc/named.rfc1912.zones [rootdns ~]# vim /etc/named.conf [rootdns ~]# cd /var/named/ [rootd…

刚购买的阿里云服务器该如何配置环境(CentOS)

文章目录 购买开始初始设置登录云服务器安装 Apache 服务安装 MySQL安装 PHP快照 第三方 SSH 登录笔者的话 购买 按照需求购买就行。学生有免费试用一个月的活动&#xff0c;可以试着玩玩。 开始初始设置 登录云服务器 购买完后&#xff0c;点击实例&#xff0c;点击实例名…

Linux下RDMA驱动程序探索系列-2

本系列文章将带领读者逐步了解Linux操作系统下的RDMA子系统。本篇文章作为系列的第二篇&#xff0c;将深入内核态驱动程序的代码&#xff0c;主要介绍如下内容&#xff1a; Driver的初始化流程几个重要verbs回调函数的简介 01、Kernel Driver的初始化流程 由于不同厂商的驱动…

进销存系统开发,含税小计和含税单价计算,含税和不含税,1000元电脑为案例

if (data ! null) {console.log("中断调试&#xff0c;2024-7-25 最终计算税务");//删除不需要会报错var 未来之窗_人工智能_计算_税额 parseFloat((data.price * data.num * data.tax_rate / 100 ).toFixed(2));var 未来之窗_人工智能_计算_含税小计 parseFloat((…

js轮播图制作

实现一个简单的JavaScript轮播图可以通过以下步骤完成&#xff1a; 创建HTML结构&#xff0c;包括轮播图容器和图片列表。 使用CSS进行样式设置&#xff0c;包括隐藏多余的图片。 使用JavaScript编写函数来控制图片的切换。

07-15 周一 lmdeploy导出迁移因子到量化模型中

07-15 周一 lmdeploy导出迁移因子到量化模型中 时间版本修改人描述2024年7月15日14:57:02V0.1宋全恒新建文档 简介 方案设计 由于norm层的前后导致smoothquant执行量化不好融合&#xff0c;为了降低我事先的难度&#xff0c;所以就不再融合normalization的算子了&#xff0c…

vue3编程-import.meta.glob实现动态路由(菜单)

import.meta.glob 是vite提供的批量懒加载组件的方法 本地开发环境&#xff1a; const modules import.meta.glob(../views/**/*.vue)这段代码返回的modules是一个Map&#xff1a; key是vue文件的相对路径&#xff0c;值是一个函数&#xff0c;将函数打印出来&#xff0c;如…

【微信小程序实战教程】之微信小程序原生开发详解

微信小程序原生开发详解 微信小程序的更新迭代非常频繁&#xff0c;几乎每个月都会有新版本发布&#xff0c;这就会让初学者感觉到学习的压力和难度。其实&#xff0c;我们小程序的每次版本迭代都是在现有小程序架构基础之上进行更新的&#xff0c;如果想要学好小程序开发技术&…

配置mysql8.0.21版本docker-compose启动容器

1. 总览 2 docker-compose.xml配置 version: 3 services:mysql:image: 192.168.188.131:8000/mysqlrestart: alwaysvolumes:- ./data:/var/lib/mysql- ./my.cnf:/etc/mysql/my.cnf- ./mysql-files:/var/lib/mysql-files- ./log/mysql:/var/log/mysqlenvironment:MYSQL_ROOT_PA…

Shell实现服务自动部署

一、环境 注意&#xff1a; nfs.example.com应该为nfs.exam.com 172.25.250.101-172.25.250.105 共 5 个 IP 地址由servera.exam.com服务器进行提供。 172.25.250.106 由 serverb.exam.com 服务器进行提供。 二、需求 项目需求&#xff1a; 1. 172.25.250.101 主机上的 W…

UEFI DebugLib 介绍

1.我们调试中常用Debug 打印信息&#xff0c;这些会输出到BIOS串口日志中 EFI_STATUSEFIAPIHelloWorld2(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE *SystemTable){EFI_STATUS Status;StatusEFI_SUCCESS;gST->ConOut->OutputString(gST->ConOut,L&q…

如何保护您的 WordPress 不被黑?

明月可以说是见到过太多 WordPress 网站被黑的示例了&#xff0c;加上平时明月也会接一些 WordPress 疑难杂症的解决服务订单&#xff0c;所以这方面绝对是专业对口了。作为一个资深 WordPress 博客站长&#xff0c;谁都有被黑过的经历&#xff0c;都是一步步走过来的&#xff…