割 点 算 法

在一个无向连通图中,如果删除某一顶点后,图再连通(即任意两点之间不能相互到达),我们称这样的顶点为割点(或者割顶)。

首先我们从图中任意一个点开始对图进行遍历,而对图进行深度优先遍历将会得到这个图的一个生成树(这个生成树并不一定是最小生成树),我们用数组 num 记录某个顶点是第几个被访问过的即 “时间戳”。

如何在遍历的时候认定一个顶点是割点,假如我们在深度优先遍历时访问到了 k 点,此时就会被 k 点分割成为两个部分。一部分是访问过的点一部分是未被访问过的点。 如果 k 点是割点,那么剩下的没有被访问过的点中至少会有一个点在不经过 k 点的情况下,是无论如何再也回不到已访问过的点,那么一个连通图就会被 k 点分割成多个不连用的子图。

到的这里我们需要解决的是,顶点 v 在不经过父顶点 u 的情况下还能回到祖先,我们的方法是对顶点v 在进行一次深度优先遍历,但是此次遍历时不允许 经过父节点 u ,看是否能回到祖先,如果不能那么这个点 u 就是割点。

前面我们遍历时已经记录生成树中每个数的时间戳,为了不重复计算,我们需要一个数组 low 来记录每个顶点在不经过父节点时能够回到的最小的时间戳。

对于一个顶点 u ,如果存过至少一个顶点 v (即顶点 u 的儿子)是的 low [v]>=num[u],即不能回到祖先,那么 u 为割点。

具体代码模板如下:

#include<iostream>
#include<algorithm>
using namespace std;
int n,m,e[10][10],root;
int num[10],low[10],flog[10],inder;
int min(int a,int b)
{
	return a<b?a:b; 
}
void dfs(int now1,int father)
{
	int child=0,i;   // child 用来记录在生成树中当前顶点 now1 的儿子个数 
	inder++;
	num[now1]=inder;  // 当前顶点的时间戳 
	low[now1]=inder;   // 当前顶点 now1 能够访问到的最早顶点的时间戳,刚开始就是自己 
	for(i=1;i<=n;i++)   // 枚举与当前顶点now1 有边相连的顶点 i 
	{
		if(e[now1][i]==1)  
		{
			if(num[i]==0)	//  如果顶点 i 的时间戳为 0 ,说明顶点i还没有被访问过 
			{
				child++;  // 从生成树的角度来说,此时 i 为 now1 的儿子 
				dfs(i,now1);  // 继续往下深度优先遍历 
				// 更新当前顶点 now1 能访问的最早的时间戳
				low[now1]=min(low[now1],low[i]);
				// 如果当前顶点不是根节点并且满足 low[i] >= num[now1],则当前顶点为割点
				if(now1!=root&&low[i]>=num[now1])
				{
					flog[now1]=1;
				} 
				// 如果当前顶点是根节点,在生成树中根节点必须要有两个儿子,那么这个根节点才能是割点
				if(now1==root&&child==2)
				{
					flog[now1]=1;
				} 
			}
			// 否则如果顶点 i 曾经被访问过,并且这个顶点不是当前顶点 now1 的父亲
			// 则说明此时的 i 为now1 的祖先,因此需要更新当前节点 now1 能访问到最早顶点的时间戳 
			else if(i!=father)
			{
				low[now1]=min(low[now1],num[i]); 
			}
		}
	 } 
	 return;
}  
int main()
{
	int i,j,x,y;
	cin>>n>>m;
	for(i=1;i<=n;i++)
	{
		for(j=1;j<=n;j++)
		{
			e[i][j]=0;
		}
	}
	for(i=1;i<=m;i++)
	{
		cin>>x>>y;
		e[x][y]=1;
		e[y][x]=1;
	 } 
	root=1;
	dfs(1,root);   //从  1  号顶点开始进行深度优先遍历
	for(i=1;i<=n;i++)
	{
		if(flog[i]==1)
		{
			cout<<i<<"\n";
		}
	}
	return 0; 
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

风遥~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值