割点——杨子曰算法

割点——杨子曰算法

瞎BB:
✌,破纪录——最短的博客名!!!!


首先,来讲一讲What is 割点?赶紧度娘一下

在一个无向图中,如果有一个顶点集合,删除这个顶点集合以及这个集合中所有顶点相关联的边以后,图的连通分量增多,就称这个点集为割点集合。
如果某个割点集合只含有一个顶点X(也即{X}是一个割点集合),那么X称为一个割点。

吼吼吼,胆子小的人已经关掉了博客,神马鬼!!!
当然,不要看那些只有大佬看得懂的东西,我们说人话,杨子曰:在一个连通图中,如果把这个点删掉后这个图就不连通了,那么这个点就是割点,也就是说,割点可能有多个,秒懂!话说在这个世界上还有一个东西叫做割边——我觉得智商正常的人都可以猜出这是啥了,我们不多说了

现在我们要讨论的就是割点怎么求了:
先膜拜大佬Tarjan,没错他开发了大量算法(如:一遍DFS求出LCA,缩点(戳我),证明并查集合并的复杂度是O(α(n))(不要问我这是啥))
今天我们要求割点的方法也是他搞出来的——一遍DFS求出所有割点
你先要知道一个东西,叫做DFS树,Look at the 图:
这里写图片描述
我觉得就不用多解释了吧,实线表示的是真实的DFS顺序,虚线是虽然没有DFS到,但它们两点间确实有边,
首先问个问题,虚线边会不会链接两颗子树(横叉边),Of course not!
如果有这条虚线边,就说明原图中这两点见右边,如果有变那么通过这个点就可以直接DFS的另一个点,那么这两个点就会成父子关系,So,杨子曰:DFS树中没有横叉边,也就是说虚线只会连接到这个节点的祖先
那现在就是怎么求割点了,在DFS的时候分两种情况讨论:
1.这个点是根节点,也就是第一个被遍历的点,如果他的子树个数≥2,他就是割点,应该很好理解,如果删掉这个点,他的子树之间就没有边了

2.这个点不是根节点,那就要看删去以后它的子树是否与这个节点的祖先有边,也就是看这个节点的子树中有没有虚线连到这个节点的祖先,如果有说明即使这个节点删掉了,子树也不会孤立,这个点不是割点,否则这个节点如果删掉,这棵子树就孤立了
那么这时候,我们就可以用low[i]表示以i为根结点的这棵子树中的结点中虚线边连到最高节点,这个最高结点的DFS序(用dfn数组存下)
那么更新low[k](k下一个DFS结点是v)分两种情况
1.k是v的祖先(也就是遍历到k时,v还没有被遍历过)

low[k]=min(low[k],low[v]);

这时候如果low[v]>=dfn[k]那么k就是割点

2.v是k的祖先(也就是遍历到k时,v已经被遍历过了)

low[k]=min(low[k],dfn[v])

OK,完事


poj1144——求割点数量
c++代码:

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;

int min(int a,int b){
	return a<b?a:b;
}

struct Edge{
	int next,to;
}edge[10005];

int head[105],low[105],dfn[105],cut[105],nedge,n,res,cnt;

void addedge(int a,int b){
	edge[nedge].to=b;
	edge[nedge].next=head[a];
	head[a]=nedge++;
}

void dfs(int k,int fa){
	int son=0; 
	low[k]=dfn[k]=++cnt;
	for (int i=head[k];i!=-1;i=edge[i].next){
		int v=edge[i].to;
		if (v==fa) continue;
		if (low[v]==0){
			son++;
			dfs(v,k);
			low[k]=min(low[k],low[v]);
			if ((k==1 && son>=2)||(k!=1 && low[v]>=dfn[k])) cut[k]=1;
		}
		else low[k]=min(low[k],dfn[v]);
	}
}

int main(){
	scanf("%d",&n);
	while(n!=0){
		memset(head,-1,sizeof(head));
		memset(low,0,sizeof(low));
		memset(dfn,0,sizeof(dfn));
		memset(cut,0,sizeof(cut));
		nedge=1;
		res=0;
		cnt=0;
		int k;
		scanf("%d",&k);
		while(k!=0){
			int a;
			char ch;
			scanf("%d%c",&a,&ch);
			while(ch!='\n'){
				addedge(a,k);
				addedge(k,a);
				scanf("%d%c",&a,&ch);
			}
			addedge(a,k);
			addedge(k,a);
			scanf("%d",&k);
		}
		dfs(1,1);
		for (int i=1;i<=n;i++){
			res+=cut[i];
		}
		cout<<res<<endl;
		scanf("%d",&n);
	}
	return 0;
}

于TJQ高层小区
未经作者允许,严禁转载:https://blog.csdn.net/HenryYang2018/article/details/80158665

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值