C++学习笔记——图论

这篇具有很好参考价值的文章主要介绍了C++学习笔记——图论。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、建图

图是由节点(v)和边(e)组成的,把图记作二元组G=(v,e)

1、基本概念

无向图                                                                    有向图

边是双向的,v1可以到v2,v2也能到v1                 边是单向的,v1可以到v2,但v2不能到v1

C++学习笔记——图论,C++,学习,笔记,图论C++学习笔记——图论,C++,学习,笔记,图论

无权图                                                                    有权图

所有边的权重都一样,都等于1                              边的权重不一样,不同应用里权重的含义不一样

C++学习笔记——图论,C++,学习,笔记,图论C++学习笔记——图论,C++,学习,笔记,图论

代码实现: 

2、储存方式

无相无权图(边都是双向的,边的权重都相等)及其对应的邻接表(一种数据结构)

C++学习笔记——图论,C++,学习,笔记,图论C++学习笔记——图论,C++,学习,笔记,图论

有向无权图及其对应的邻接矩阵

C++学习笔记——图论,C++,学习,笔记,图论C++学习笔记——图论,C++,学习,笔记,图论

代码实现:

//邻接矩阵初始化 
for(int i=1;i<=n1;i++) //n1为数组第一维大小
{
	for(int j=1;j<n2;j++)  //n2为数组第二维大小 
	{
		g[i][j]=g[j][i]=0;
	}
}
//储存邻接矩阵数据 
cin>>n>>m;  //n表示点的数量,m表示边的数量 
for(int i=1;i<=m;i++)  //枚举输入边 
{
	cin>>x>>y>>z;   
	dis[x][y]=z;  //有向边 
	dis[x][y]=dis[y][x]=z;  //无向边 
}

二、最短路

1、Dijkstra(取最短)

       算法原理

C++学习笔记——图论,C++,学习,笔记,图论

        以①为起点,先假设到所有节点的距离都是正无穷,依次向与之相连的②和③计算,到②的距离是3,比正无穷小,则距离更新为3,到③的距离是4,此时将①标记(已经访问过了);接着将②与③对比,到②的距离更小,则以②为起点,接着访问与②相邻的节点(不访问已经标记过的),更新距离,更新完后将②标记,以此类推,直到所有节点距离更新完毕,所有节点都被访问过为止。

        该算法只适用于非负权图,如果边的权重是负数的话,用这个算法算出来的结果是错误的。

       代码实现

#include <iostream>
using namespace std;
const int inf=0x7fffffff;  //定义inf为无穷大 
int n,e,s;                 //n个点,e条边,s是原点 
int dis[101],check[101];   //距离,有没有被标记过 
int graph[101][101];       //邻接矩阵存储一个图 
int main()
{
	for(int i=1;i<=100;i++)   //距离初始化为无穷大 
	{
		dis[i]=inf;
	}
	cin>>n>>e;                //读入点和边的数量 
	for(int i=1;i<=e;i++)     //读入每一条边的数据 
	{
		int a,b,c;
		cin>>a>>b>>c;          
		graph[a][b]=c;        //从a到b的边权是c
	}
	cin>>s;                   //读入原点 
	dis[s]=0;                 //原点到原点距离更新为0 
	for(int i=1;i<=n;i++)     //要把所有点check一遍 
	{
		int minn=inf,minx;    //最短距离设置为无穷大,minx储存点的编号 
		for(int j=1;j<=n;j++) //遍历所有的点 
		{
			if(dis[j]<minn&&check[j]==0) //距离更小且没被标记 
			{
				minn=dis[j],minx=j;  //当前最小值更新为距离,记录点的编号 
			}
		}
		check[minx]=1;  //已经是最短距离了,标记它 
		for(int j=1;j<=n;j++)  //再找一遍所有的点 
		{
			if(graph[minx][j]>0)  //这个点和最小距离的点有连边 
			{
				if(minn+graph[minx][j]<dis[j])  //如果绕路的距离更小 
				{
					dis[j]=minn+graph[minx][j];  //更新距离 
				}
			}
		}
	}
	for(int i=1;i<=n;i++)  //输出每个点的距离 
	{
		cout<<dis[i]<<" ";
	}
	return 0;
}

2、floyd(中间点)

       算法原理

C++学习笔记——图论,C++,学习,笔记,图论C++学习笔记——图论,C++,学习,笔记,图论

        D数组代表的是两个点之间的边权(即邻接矩阵),Path数组表示终点的前一个点(比如从0到1,1为终点,它的前一个点是0)

C++学习笔记——图论,C++,学习,笔记,图论

        接着我们以1作为中间点,也就是从一个点到另一个点我们都要求经过1。如果说经过1这个点的路径会比原来的路径更短,那我们就更新这条路径为最短路径,否则保留原本路径的数据。

C++学习笔记——图论,C++,学习,笔记,图论

继续递推,直到把所有的点都作为中间点更新完之后,得到的矩阵就是最短路径。 

       代码实现

#include <iostream>
using namespace std;

const int N=210,M=20010,inf=0x3f3f3f3f;  //最多 N个节点,M条边 
int n,m,q;  //n个节点,m条边,q个询问 
int d[N][N];  //节点之间的距离 

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++)  //初始化每个节点之间的距离为无穷大,节点本身的距离是0 
	{
		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<<"impossible";  //两个节点之间没有路径,输出impossible 
		else cout<<d[a][b];  //输出最小路径 
	}
	return 0;
}

三、最小生成树

树的概念是所有节点都不孤立,但又不形成闭环。

1、Kruskal(权值从小到大排列)

算法原理

C++学习笔记——图论,C++,学习,笔记,图论

先将所有的点和边取出,放入一个列表中,再按边权值从小到大排列

C++学习笔记——图论,C++,学习,笔记,图论C++学习笔记——图论,C++,学习,笔记,图论

再按照边的权值每次取出一条边回贴到图中,每回贴一条边,都要判断是否形成闭环,如果没有形成环,那这条边就选入最小生成树,如果形成了环,这条边就丢弃,继续回贴下一条边。

C++学习笔记——图论,C++,学习,笔记,图论

直到选择了n-1条边,则最小生成树已经构建完成

代码实现

#include <iostream>
#include <bits/stdc++.h> 
using namespace std;

const int maxn=222222;
int N,M;           //顶点和边的个数  
int ans=0;         //最小生成树边权之和 
int Num_Edge=0;    //最小生成树当前边数 
int father[maxn];  //并查集 
 
struct edge{
	int u,v,cost;  //两个端点和他们边的权值 
}E[maxn];          //最多有maxn条边 

bool cmp(edge a,edge b)  //自定义升序函数 
{
	return a.cost<b.cost; //边的权值从小到大排序 
}

int findFather(int x)  //查询顶点+路径压缩 
{
	if(father[x]!=x) father[x]=findFather(father[x]);
	return father[x];
} 

void kruskal()
{
	for(int i=1;i<=N;i++)  //并查集初始化 
	{
		father[i]=i; 
	}
	sort(E,E+M,cmp);  //按边权从小到大排序
    for(int i=0;i<=M;i++)  //枚举所有边 
    {
    	if(findFather(E[i].u)!=findFather(E[i].v))  //若不在一个集合中 
    	{
    		father[findFather(E[i].u)]=findFather(E[i].v);  //合并集合 
    		ans+=E[i].cost;  //边权之和增加 
    		++Num_Edge;  //当前生成树边数增加 
    		if(Num_Edge==N-1) break;  //边数等于顶点数-1,跳出 
		}
	}
	if(Num_Edge==N-1) cout<<ans;  //返回最小生成树边权之和 
	else cout<<"-1";  //无法连通时返回-1 
} 

int main()
{
	cin>>N>>M;
	for(int i=0;i<M;i++)
	{
		cin>>E[i].u>>E[i].v>>E[i].cost;
	}
	kruskal();
	return 0;
}

2、Prim(两个区域间的最短路)

算法原理

C++学习笔记——图论,C++,学习,笔记,图论

C++学习笔记——图论,C++,学习,笔记,图论

node表示节点,selected表示概念是否被选入最小生成树,mindist表示从原点到该点的距离,parent表示当前点的上一个节点。

先将原点加入最小生成树最为顶点,扫描这个点连接的边,如果边的权值小于列表中的值,则将权值输入到列表里更新数据,接着扫描列表,找到最小权值,将这条边连接的节点纳入最小生成树,以这个点为新的顶点,进行下一轮更新。

C++学习笔记——图论,C++,学习,笔记,图论

直到已选顶点的集合包括了所有的节点,最小生成树构建完毕。文章来源地址https://www.toymoban.com/news/detail-848763.html

代码实现

#include <bits/stdc++.h> 
#define inf 1e8
using namespace std;

const int maxn=1005;
int graph[maxn][maxn];  //邻接矩阵 
int dis[maxn];  //顶点到生成树的距离 
bool vis[maxn];  //标记顶点是否已经加入生成树 

int Prim(int n)  //n为顶点数 
{
	int ans=0;  //最小生成树边权之和 
	memset(vis,false,sizeof(vis));  //初始化 
	memset(dis,inf,sizeof(dis));  //初始化 
	dis[1]=0;  //从哪个顶点开始都可以 
	for(int i=1;i<=n;i++)
	{
		int u=-1;
		for(int j=1;j<=n;j++)
		{
			if(!vis[j]&&(u==-1||dis[u]>dis[j]))
			{
				u=j;  //找到最小距离的点 
			}
			vis[u]=true;  //标记该点加入生成树 
			ans+=dis[u];
			for(int v=1;v<=n;v++)
			{
				if(!vis[v]&&graph[u][v]<dis[v])  //更新未加入生成树的点到生成树的距离 
				{
					dis[v]=graph[u][v];
				}
			}

		}
	}
	return ans;	
}

int main()
{
	int n,m;
	cin>>n>>m;  //顶点和边数 
	memset(graph,inf,sizeof(graph));  //初始化 
	for(int i=1;i<=m;++i)
	{
		int u,v,w;
		cin>>u>>v>>w;  //读入边权 
		graph[u][v]=graph[v][u]=w;  //更新邻接矩阵 
	}
	int ans=Prim(n);  //计算最小生成树的权值和 
	cout<<ans<<endl;
	return 0;
}

到了这里,关于C++学习笔记——图论的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 图论——Kruskal重构树 学习笔记

    最大生成树将部分内容倒置即可 基本信息 求解最小生成树 时间复杂度: (O(m log m)) 更适合稀疏图 算法思想 按照边权从小到大排序 依次枚举每一条边,如果这一条边两侧不连通,则加入这条边 代码 点击查看代码 算法思想 在构建最小生成树的时候,设现在枚举到了一条要

    2024年02月08日
    浏览(37)
  • MIT6.024学习笔记(三)——图论(2)

    科学是使人变得勇敢的最好途径。——布鲁诺 在通信网络中,分为主机和路由器两部分,我们将主机分为输入端和输出端,则构成的图中有三部分:路由器、输入端、输出端,构成了一个有向图。那么,一个N*N规模的通信网络,应该怎么构成才能达到性能最佳呢(假设N总是

    2024年02月09日
    浏览(48)
  • 《图论与网络优化》学习笔记(第1-5章)

    上课时间:2023年10月——2024年1月 上课地点:国防科技大学 授课老师:戴丽 教材:戴丽.《图论与网络优化》 注:部分笔记根据自己的理解进行改动,可能不是很严谨,但便于理解。 1.1 图论的起源与发展 1736年,Euler研究并解决了konigsberg七桥问题(格林森堡七桥问题)。2

    2024年01月23日
    浏览(59)
  • 《图论与网络优化》学习笔记(第6-8章)

    上课时间:2023年10月——2024年1月 上课地点:国防科技大学 授课老师:戴丽 教材:戴丽.《图论与网络优化》 注:部分笔记根据自己的理解进行改动,可能不是很严谨,但便于理解。 6.1 独立集和覆盖 独立集 :设 S ⊆ V ( G ) Ssubseteq V(G) S ⊆ V ( G ) ,若 S S S 中任意两个顶点在

    2024年02月19日
    浏览(42)
  • [图论]哈尔滨工业大学(哈工大 HIT)学习笔记32-39

    视频来源:6.1.1 树的定义_哔哩哔哩_bilibili 目录 1. 树的定义 2. 树的性质 3. 极小连通图 4. 树的中心 5. 生成树 6. 最小生成树 7. 割点 8. 割点的性质 (1)定义:一个连通的无圈的图称为树 (2)平凡树:只有一个顶点的树 (3)推论1:非平凡树至少有两个叶子( ? ) (4)推论

    2024年02月08日
    浏览(49)
  • [图论]哈尔滨工业大学(哈工大 HIT)学习笔记23-31

    视频来源:4.1.1 背景_哔哩哔哩_bilibili 目录 1. 哈密顿图 1.1. 背景 1.2. 哈氏图 2. 邻接矩阵/邻接表 3. 关联矩阵 3.1. 定义 4. 带权图 (1)以地球为建模,从一个大城市开始遍历其他大城市并且返回,每个顶点只能被通过一次 (1)定义:如果G中有生成圈,则称G为哈氏图 (2)和欧

    2024年02月22日
    浏览(56)
  • 利用GAT(图论分析工具箱)构建并分析大脑结构协变网络学习笔记

    前面我学习了利用DTI构建白质纤维脑网络,并采用GRETNA计算了小世界网络属性。阅读文献发现可以利用灰质体积或皮层指标(皮层厚度、折叠指数、沟深)等构建结构协变网络再进行网络拓扑属性的计算。因此,我采用前面提取的灰质体积和皮质数据进行了结构协变网络分析

    2024年04月16日
    浏览(101)
  • 【C++算法竞赛 · 图论】图论基础

    前言 图论基础 图的相关概念 图的定义 图的分类 按数量分类: 按边的类型分类: 边权 简单图 度 路径 连通 无向图 有向图 图的存储 方法概述 代码 复杂度 图论(Graph theory) ,是 OI 中的一样很大的一个模块,围绕它有很多高难度的算法以及高级的概念。 这篇文章将介绍关

    2024年04月12日
    浏览(42)
  • C++学习笔记——从面试题出发学习C++

    C++博大精深,在学习过程中我也有看过《Effective C++》、《Efficient C++》、《C++ Prime》这样一些C++的经典大作,但是个人感觉是由于语法太多,很难抓住重点,在工作中如果不很经常用到某个语法,即使在书籍上有看过也会很快忘记。而刷面试题是一个很好的查漏补缺的方式,

    2024年02月13日
    浏览(42)
  • C++学习笔记(三十四):c++ array

    本节介绍c++标准库模板中的array和c风格的array的区别,及什么时候使用std::array。

    2024年01月23日
    浏览(51)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包