2025年河南省暑期青少年程序设计能力提升集训DAY4图论基础

图的基本概念

以下是一些关于图的概念,记住总比不记要好,过于基础,可以跳过

图:图是一种由一组顶点和一组边组成的数据结构,记做 ,其中 代表顶点集合 代表边 集合。

顶点 :顶点是图的基本单位,也称为节点。

边 :一条边是连接两个顶点的线段或弧。可以是无向的,也可以是有向的。一条边可以记做为 。 在无向图中,若存在一条 ,表示可以从 点直接走到 点,反之同理。但若在有向图中,存在一条 边 ,表示可以从 节点直接走向 节点。

无向图:图中的边没有方向,即 和 是同一条边。

有向图:图中的边有方向,即 和 不是同一条边。

简单图:表示不含有重边(两个顶点之间的多条边)和自环(顶点到自身的边)的图。

多重图:允许有重边和自环的图。

边权 :一般表示经过这一条边的代价(代价一般是由命题人定义的)。 如下图,就是一个有向的简单图(通常来说,在有向图中边的方向用箭头来表示):

度数:一个顶点的度是连接该顶点的边的数量。在有向图中,有入度和出度之分(具体例子见后文)。

路径:从一个顶点到另一个顶点的顶点序列,路径上的边没有重复。

回路:起点和终点相同的路径。

连通图:任意两个顶点之间都有路径相连的无向图。

强连通图 :任意两个顶点之间都有路径相连的有向图。


图的表示方法

现在常用的遍历方法是邻接表和邻接矩阵,接下来将其分别说明

邻接矩阵

对于有向边E=(1,2)(2,3)(3,2),则邻接矩阵如图所示

0123
1110
2011
3011

 代码示例

int a[51][51];//建立表
for(int i=1;i<=50;i++){
    for(int j=1;j<=50;j++){
        a[i][j]=0;//初始化,(全局变量不用赋)
    }
}
a[1][2]=a[2][3]=a[3][2]=1;//赋值

邻接表

对于有向边E=(1,2)(2,3)(3,2),则邻接表如图所示

顶点数组: 
索引 | 顶点 | 相邻顶点链表 (头指针)
-----|------|---------------------------
  1  |  1  | -> [2] -> NULL
  2  |  2  | -> [3] -> NULL
  3  |  3  | -> [2] -> NULLd

代码示例:

vector<int> G[50]; // 建图。
G[1].push_back(2);
G[2].push_back(3);
G[3].push_back(2);
G[3].push_back(4);

对比下来

特性邻接矩阵邻接表
空间复杂度O(V²)O(V + E)
查边效率O(1)(直接访问矩阵元素)O(degree(V))(遍历链表)
添加边O(1)O(1)(链表头插)
遍历邻接点O(V)(需扫描整行)O(degree(V))
适用场景稠密图(边数接近V²)稀疏图

图的遍历

利用邻接矩阵,我们可以如此解决

int vis[105],map[105][105];
void dfs(int node){
    if(vis[node]) return ;
    vis[node]=1;
    cout<<node<<endl;
    for(int i=1;i<=n;i++)
    if (map[node][i]!=-0x7f7f7f7f) dfs(i);
    return ;
}

利用邻接表,我们可以如此解决

#include <vector>
vector<int> G[105];
int vis[105];
void dfs(int node){
    if(vis[node]) return ;
    vis[node]=1;
    cout<<node<<endl;
    for(auto to:G[node]) dfs(to);
    return ;
}

并查集

并查集(Disjoint Set Union, DSU)是一种用于高效处理动态连通性问题的数据结构,尤其适合管理元素分组和合并操作,对于具体原理这里不在说明,只给出应用及代码

赋值

int f[N];//f[x]表示x的父节点是谁
for(int i=1;i<=n;i++) f[i]=i;

查询

int getf(int x){return f[x]==x?x:getf(f[x]);}

优化

这里使用路径压缩,当然对于需要保留节点关系的题需要使用启发式合并

int getf(int x){ return f[x]==x?x:f[x]=getf(f[x]);}//更新父亲节点即可

 下面是启发式合并

void merge(int x,int y){
    x=getf(x);y=getf(y);
    if(x==y) return;
    if(size[x]>size[y]) swap(x,y);
    f[x]=y;
    size[x]+=size[y];
}

合并

f[getf(x)]=getf(y);

拓扑排序

拓扑排序(Topological Sort)是针对有向无环图(DAG)的线性排序算法,其核心思想是满足图中所有边的方向性要求

下面进行解释

        

这张图解释的很详细了,找入度为0的删去,拓扑序为1 2 4 3 5 

具体代码如下

void topsort(){
	queue<ll> q;
	for(int i=1;i<=n;i++) if(du[i]==0) q.push(i);
	while(!q.empty()){
		ll x=q.front(); q.pop();
		for(auto t:a[x]){
			ll y=t.first,v=t.second;
			du[y]--;
			if(c[x]>0) c[y]+=c[x]*v;
			if(!du[y]){
				c[y]-=u[y];
				q.push(y);
			}
		}
	}
}

(今天没什么时间,题就不说了。并不是我不想写,我没有水博客!

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值