图论模板详解

目录

Floyd算法

例题:蓝桥公园

Dijkstra算法

例题:蓝桥王国 

SPFA算法

例题:随机数据下的最短路问题

总结

最小生成树MST

Prim算法

Kruskal算法

例题:聪明的猴子

Floyd算法

最简单的最短路径算法,使用邻接矩阵存图,因为Floyd算法计算的结果是所有点对之间的最短路,本身就要n^{2}的空间,用矩阵存储最合适。效率不高,计算复杂度为O\left ( n^{3} \right ),只能用于n<300的小规模的图,不能用于大图,在某些场景下有自己的优势,难以替代,能做传递闭包问题。

for(int k=1;k<=n;k++){
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			dp[i][j]=min(dp[i][j],d[i][k]+dp[k][j]);
		}
	}
} 

Floyd算法是多源最短路算法,以此计算能得到图中每一对结点之间(多对多)的最短路径。

Floyd算法能判断负圈,若图中有权值为负的边,某个经过这个负边的环路,所有边长相加的总长度也是负数,这就是负圈。在这个负圈上每绕一圈,总长度就更小,从而陷入在兜圈子的死循环。Floyd算法很容易判断负圈,只要在算法运行过程中出现任意一个dp[i][j]<0就说明有负圈,因为dp[i][j]是从i出发,经过其它中转点绕一圈回到自己的最短路径,如果等于0,就存在负圈。

例题:蓝桥公园

#include<bits/stdc++.h>
using namespace std;
const long long INF=0x3f3f3f3f3f3f3f3fLL;
const int N=405;
long long dp[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++){
				dp[i][j]=min(dp[i][j],dp[i][k]+dp[k][j]);
			}
		}
	}
}
int main(){
	cin>>n>>m>>q;
	memset(dp,0x3f,sizeof(dp));
	for(int i=1;i<=m;i++){
		int u,v;
		long long w;
		cin>>u>>v>>w;
		dp[u][v]=dp[v][u]=min(dp[u][v],w);
	}
	floyd();
	while(q--){
		int s,t;
		cin>>s>>t;
		if(dp[s][t]==INF){
			cout<<"-1"<<endl;
		}
		else if(s==t){
			cout<<"0"<<endl;
		}
		else{
			cout<<dp[s][t]<<endl;
		}
	}
	return 0;
}

Dijkstra算法

Dijkstra算法用于求解单源最短路径问题,非常高效而且稳定,但是只能处理不含负权边的图。

Dijkstra算法是贪心思想实现的,首先把起点到所有点的距离存下来找个最短的,然后松弛一次再找出最短的,所谓的松弛操作就是,遍历一遍看通过刚刚找到的距离最短的点作为中转站会不会更近,如果更近了就更新距离,这样把所有的点找遍之后就存下了起点到其它所有点的最短距离。

采用优先队列实现,每次往队列中放数据时,按从小到大的顺序放,采用小顶堆的方式,复杂度是O\left ( logn \right ),保证最小的数总在最前面。找最小值,直接取第一个数,复杂度是O\left ( 1 \right )

例题:蓝桥王国 

#include<bits/stdc++.h>
using namespace std;
const long long INF=0x3f3f3f3f3f3f3f3fLL;
const int N=3e5+2;
struct edge{
	int from,to;
	long long w;
	edge(int a,int b,long long c){
		from=a;
		to=b;
		w=c;
	}
};
vector<edge>e[N];
struct s_node{
	int id;
	long long n_dis;
	s_node(int b,long long c){
		id=b;
		n_dis=c;
	}
	bool operator < (const s_node &a) const{ 
		return n_dis>a.n_dis;
	}
};
int n,m;
int pre[N];
void print_path(int s,int t){
	if(s==t){
		printf("%d ",s);
		return;
	}
	print_path(s,pre[t]);
	printf("%d ",t);
}
long long dis[N];
void dijkstra(){
	int s=1;
	bool done[N];
	for(int i=1;i<=n;i++){
		dis[i]=INF;
		done[i]=false;
	}
	dis[s]=0;
	priority_queue<s_node>Q;
	Q.push(s_node(s,dis[s]));
	while(!Q.empty()){
		s_node u=Q.top();
		Q.pop();
		if(done[u.id]){
			continue;
		}
		done[u.id]=true;
		for(int i=0;i<e[u.id].size();i++){
			edge y=e[u.id][i];
			if(done[y.to]){
				continue;
			}
			if(dis[y.to]>y.w+u.n_dis){
				dis[y.to]=y.w+u.n_dis;
				Q.push(s_node(y.to,dis[y.to]));
				pre[y.to]=u.id;
			}
		}
	}
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		e[i].clear();
	}
	while(m--){
		int u,v,w;
		cin>>u>>v>>w;
		e[u].push_back(edge(u,v,w));
	}
	dijkstra();
	for(int i=1;i<=n;i++){
		if(dis[i]>=INF){
			cout<<"-1";
		}
		else{
			cout<<dis[i];
		}
	}
	return 0;
}

SPFA算法

SPFA算法=队列处理+Bellman-Ford

Bellman-Ford算法有很多低效或无效的操作,其核心内容,是在每一轮操作中,更新所有节点到起点s的最短距离。

计算和调整一个节点u到s的最短距离后,如果紧接着调整u的邻居节点,这些邻居肯定有新的计算结果,而如果漫无目的的计算不与u相邻的节点,这可能毫无变化,所以这些操作是低效的。

改进:计算结点u之后,下一步只计算和调整它的邻居,能加速收敛的过程。这些步骤用队列操作

例题:随机数据下的最短路问题

 

#include<bits/stdc++.h>
using namespace std;
const long long INF=0x3f3f3f3f3f3f3f3f;
const int N=5e3+10;
struct edge{
	int to;
	long long w;
	edge(int tt,long long ww){
		to=tt;
		w=ww;
	}
};
long long dist[N];
int inq[N];
vector<edge>e[N];
void spfa(int s){
	memset(dist,0x3f,sizeof(dist));
	dist[s]=0;
	queue<int>q;
	q.push(s);
	inq[s]=1;
	while(!q.empty()){
		int u=q.front();
		q.pop();
		inq[u]=0;
		if(dist[u]==INF){
			continue;
		}
		for(int i=0;i<e[u].size();i++){
			int v=e[u][i].to;
			long long w=e[u][i].w;
			if(dist[v]>dist[u]+w){
				dist[v]=dist[u]+w;
				if(!inq[v]){
					q.push(v);
					inq[v]=1;
				}
			}
		}
	}
}
int main(){
	int n,m,s;
	cin>>n>>m>>s;
	for(int i=1;i<=m;i++){
		int u,v;
		long long w;
		cin>>u>>v>>w;
		e[u].push_back(edge(v,w));
	}
	spfa(s);
	for(int i=1;i<=n;i++){
		if(dist[i]==INF){
			cout<<-1;
		}
		else{
			cout<<dist[i];
		}
		if(i!=n){
			cout<<" ";
		}
		else{
			cout<<endl;
		}
	}
	return 0;
}

总结

单源最短路

(1)当权值非负时,用Dijkstra算法。

(2)当权值有负值,且没有负圈,则用SPFA。SPFA能检测负圈,但是不能输出负圈。

(3)当权值有负值而且有负圈需要输出,则用Bellman-Ford,能够检测并输出负圈。

多源最短路

使用Floyd算法。

最小生成树MST

一个含有n个结点的连通图的生成树是原图的极小连通子图,包含原图中的所有n个结点,并且边的权值之和最小。

Prim算法

对点进行贪心操作,从任意一个点u开始,把距离它最近的点加入到MST中,下一步,把距离{u,v}最近的点w加入到MST中;继续这个过程,直到所有的点都在MST中。适用于稠密图。

#include<bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f3f3f3f3f;
const int MAXN=1005;
vector<int>demo;
int closest[MAXN],lowcost[MAXN],n,m;//m为节点的个数,n为边的数量
int G[MAXN][MAXN];//邻接矩阵
int prim(){
	for(int i=0;i<n;i++){
		lowcost[i]=INF;
	}
	for(int i=0;i<m;i++){
		closest[i]=0;
	}
	closest[0]=-1;//加入第一个点,-1表示该点在集合U中,否则在集合V中
	int num=0,ans=0,e=0;
	while(num<m-1){//当点还没有全部放进去 
		int micost=INF;
		for(int i=0;i<m;i++){
			if(closest[i]!=-1){
				int temp=G[e][i];
				if(temp<lowcost[i]){
					lowcost[i]=temp;
					closest[i]=e;
				}
				if(lowcost[i]<micost){
					micost=lowcost[i];
				}
			}
			ans+=micost;
			demo.push_back(micost);
			closest[e]=-1;
			num++;
		}
	} 
	return ans;
} 
int main(){
	cin>>m>>n;
	memset(G,INF,sizeof(G));
	for(int i=0;i<n;i++){
		int a,b,c;
		cin>>a>>b>>c;
		G[b][a]=G[a][b]=c;
	}
	cout<<prim()<<endl;
	for(int i=0;i<m-1;i++){
		cout<<demo[i]<<" ";
	}
	return 0;
}

Kruskal算法

对边进行贪心操作。从最短的边开始,把它加入到MST中,在剩下的边中找最短的边,加入到        MST中,继续这个过程,直到所有的点都在MST中。适用于稀疏图。

kruskal算法的两个关键技术:

(1)对边进行排序

(2)判断圈,即处理连通性问题。这个问题用并查集简单而高效,并查集是krustral算法的绝配。

例题:聪明的猴子

#include<bits/stdc++.h>
using namespace std;
int n,m,father[1100000];
struct node{
	int x,y,k;
}Q[1100000];
int find(int x){
	if(father[x]==x){
		return x;
	}
	return father[x]=find(father[x]);
} 
bool cmp(node a,node b){
	return a.k<b.k;
}
int main(){
	cin>>n>>m;
	int sum=0,st=0;
	for(int i=0;i<m;i++){//把m条边扫描进来 
		cin>>Q[i].x>>Q[i].y>>Q[i].k;
	}
	sort(Q,Q+m,cmp);
	for(int i=1;i<=n;i++){
		father[i]=i;
	}
	for(int i=0;i<m;i++){
		int tx=find(Q[i].x);
		int ty=find(Q[i].y);
		if(tx!=ty){
			sum+=Q[i].k;
			st++;
			father[tx]=ty;
			if(st==n-1){
				break;
			}
		}
	}
	cout<<sum<<endl;
	return 0;
}

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

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

相关文章

BGP联盟、对等体组、按组打包

BGP联盟 将大的AS划分为几个子AS&#xff08;成员AS&#xff09;&#xff0c;每个子AS内部建立全连接的IBGP邻居&#xff0c;子AS之间建立EBGP邻接关系。 联盟AS&#xff1a;大AS&#xff0c;就是常说的AS号&#xff0c;一般使用公有AS号。 成员AS&#xff1a;小AS&#xff…

MongoDB 启动异常

Failed to start up WiredTiger under any compatibility version. 解决方案: 删除WiredTiger.lock 和 mongod.lock两个文件&#xff0c;在重新启动。回重新生成新的文件。

Unicode在线编码和解码工具推荐(实用)

Unicode在线编码 - Unicode编码工具 - Unicode在线生成 - Unicode在线解码 - WGCLOUD

研发效能·创享大会—IDCF五周年专场

时光流转&#xff0c;IDCF即将迎来五周年的庆典。在这个意义非凡的时刻&#xff0c;我们精心筹备了一场盛大的聚会【研发效能创享大会—IDCF五周年专场】。 IDCF自2019年成立以来&#xff0c;携手百余位技术领头人共同打造DevOps技术学习平台&#xff0c;与30万社群伙伴联动&a…

数据类型和类型检测

Data Type And Type Checking 1.编程语言中的数据类型 类型和变量 一个类型是一系列值的集合&#xff0c;这些集合可以抽象出一个相同的特点&#xff0c;并且可以相互实现计算 例如&#xff1a; 布尔类型&#xff1a;true or false整形&#xff1a;1,2,3…浮点数类型&#xf…

OM6650AM支持蓝牙5.1协议栈与2.4GHz私有协议的双模无线连接SoC芯片

OM6650AM是一款超低功耗、同时支持蓝牙5.1协议栈与2.4GHz私有协议的双模无线连接SoC芯片&#xff0c;采用4.0 mm x 4.0 mm QFN32封装&#xff0c;具有丰富的资源&#xff0c;极低的功耗&#xff0c;优异的射频性能&#xff0c;可广泛应用于车载数字钥匙模组、胎压检测、PKE钥匙…

【教程】Flutter 应用混淆

在移动应用开发中&#xff0c;保护应用代码安全至关重要。Flutter 提供了简单易用的混淆工具&#xff0c;帮助开发者在构建 release 版本应用时有效保护代码。本文将介绍如何在 Flutter 应用中使用混淆&#xff0c;并提供了相关的操作步骤和注意事项。 &#x1f4dd; 摘要 本…

YOLOv9改进策略 :主干优化 | 无需TokenMixer也能达成SOTA性能的极简ViT架构 | CVPR2023 RIFormer

💡💡💡本文改进内容: token mixer被验证能够大幅度提升性能,但典型的token mixer为自注意力机制,推理耗时长,计算代价大,而RIFormers是无需TokenMixer也能达成SOTA性能的极简ViT架构 ,在保证性能的同时足够轻量化。 💡💡💡RIFormerBlock引入到YOLOv9,多个数…

C语言第三十八弹---编译和链接

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】 编译和链接 1、翻译环境和运行环境 2、翻译环境 2.1、预处理&#xff08;预编译&#xff09; 2.2、编译 2.2.1、词法分析 2.2.2、语法分析 2.2.3、语义分…

【Linux】自定义协议+序列化+反序列化

自定义协议序列化反序列化 1.再谈 "协议"2.Cal TCP服务端2.Cal TCP客户端4.Json 喜欢的点赞&#xff0c;收藏&#xff0c;关注一下把&#xff01; 1.再谈 “协议” 协议是一种 “约定”。在前面我们说过父亲和儿子约定打电话的例子&#xff0c;不过这是感性的认识&a…

YoloV8改进策略:BackBone改进|GCNet(独家原创)|附结构图

摘要 本文使用GCNet注意力改进YoloV8,在YoloV8的主干中加入GCNet实现涨点。改进方法简单易用&#xff0c;欢迎大家使用&#xff01; 论文:《GCNet: Non-local Networks Meet Squeeze-Excitation Networks and Beyond》 非局部网络&#xff08;NLNet&#xff09;通过为每个查…

大模型日报20240401

大模型实时打《街霸》捉对PK&#xff0c;GPT-4居然不敌3.5&#xff0c;新型Benchmark火了 链接&#xff1a;https://news.miracleplus.com/share_link/22340 让大模型直接操纵格斗游戏《街霸》里的角色&#xff0c;捉对PK&#xff0c;谁更能打&#xff1f;GitHub上一种你没有见…

隧道烘箱在线粒子监测系统解决方案

关于隧道烘箱定义 隧道烘箱是一种采用长箱体热风循环以及远红外干燥方式进行干燥的设备。它主要是为了满足产量高、效率要求高的烘干干燥需求而设计的。在计算机系统的监控下&#xff0c;物品随输送带的输送依次进入隧道烘箱的预热区、高温灭菌区&#xff08;温度≥5min&#x…

C++ | Leetcode C++题解之第1题两数之和

题目&#xff1a; C 题解&#xff1a; class Solution { public:vector<int> twoSum(vector<int>& nums, int target) {unordered_map<int, int> hashtable;for (int i 0; i < nums.size(); i) {auto it hashtable.find(target - nums[i]);if (it …

4.机器学习-十大算法之一线性回归算法(LinearRegression)案例讲解

机器学习-十大算法之一线性回归算法案例讲解 一摘要二个人简介三什么是线性回归四LinearRegression使用方法五糖尿病数据线性回归预测1.数据说明2.导包3.导入数据4.脱敏处理5.抽取训练数据和预测数据6.创建模型7.预测8.线性回归评估指标9.研究每个特征和标记结果之间的关系.来分…

Java接口与继承实践:Ether通信系统的构建(day16)

创建一个接口Icontroller, 再创建一个接口IReceiver, 创建一个子类实现IReceiver&#xff0c; 创建一个子类实现IContrller&#xff0c; 创建一个类Ether 创建一个Signal类 创建一个类Radiosignal继承Signal 创建一个用户User 最后创建一个Main类 今日总结&#xff1a…

FreeRTOS 多任务系统

在最早接触嵌入式的时候&#xff0c;我们编写的代码都是在一个while循环里处理所有的事务。 int main() {while(1){do_something();do_something1();do_something2();} } 这三个事务轮流执行。逻辑简单。但会带来一个问题&#xff1a; 事务1在执行的时候&#xff0c;事务2得…

LabVIEW齿轮箱噪声监测系统

LabVIEW齿轮箱噪声监测系统 齿轮箱作为机械设备的“心脏”&#xff0c;其健康状态对设备的性能有着重要的影响。传统的齿轮箱监测方法依赖于直接的振动信号分析&#xff0c;但这种方法不仅成本高昂&#xff0c;而且在安装和拆卸过程中可能对设备造成损害。针对这些问题&#x…

激光雷达的量产车方案

文章目录 现在的量产方案共同点与差异技术方案应用场景未来发展趋势 现在的量产方案 在量产车领域&#xff0c;半固态激光雷达技术的发展和应用是实现高级自动驾驶功能的关键技术之一。半固态激光雷达&#xff0c;与传统的固态激光雷达相比&#xff0c;其最大特点是在内部采用…

基于java+springboot+vue实现的垃圾分类回收系统(文末源码+Lw)23-213

摘 要 如今社会上各行各业&#xff0c;都喜欢用自己行业的专属软件工作&#xff0c;互联网发展到这个时候&#xff0c;人们已经发现离不开了互联网。新技术的产生&#xff0c;往往能解决一些老技术的弊端问题。因为传统垃圾分类回收系统信息管理难度大&#xff0c;容错率低&a…