割点

学tarjan割点,发觉很难,不一定是tarjan算法,我只是写一下自己的理解。
割点:在一个联通无向图中,去掉一个点,使得这个连通图不连通,那么就称这个点为割点。
思路:
从一个未查过的点开始dfs,这个点设为根,查它能到的所有还未到过的点,这些点是这个点的子节点,这个点是它们的父节点,若这些点中的点可以不通过它的父节点到达已经被搜过的点,那么删去这个点的父节点对这个点到达父节点无影响,但是如果反之有影响,那么这个点就是割点(根节点是割点的判断方式是有至少两个互不相通的子节点)。
判断一个点能不能到搜过的点的方式是dfn记录这个点是第几个被搜到,low记录能到的dfn最小的点(不准确,搜u点时若v为被搜过,那么low[u]=min(low[u],low[v]),但是如果v被搜过,low[u]=min(low[u],dfn[v]))。
注意:
1.整个图可能是非连通图,需要把它分成连通图做。
2.一个点s到的搜过的点t,low取min,假设t经过几个点可以回到节点r,那么它回溯传回去,回溯到一个点u,low[v]=r第几个被搜,r比u早搜到,这个情况应当不能判断u是割点,但是若点u是t到r的必经之路,那么就可能可以判断u是割点了。
代码:

#include<iostream>
#include<algorithm>
using namespace std;
int n,m;
struct node{
	int to;
	int nxt;
}edge[200010];
int head[100010],tot; 
void addedge(int u,int v){
	edge[++tot].to=v;
	edge[tot].nxt=head[u];
	head[u]=tot;
}
int dfn[100010],low[100010],cnt;
int ans[100010],cmt;
bool cmp(int x,int y){
	return x<y;
}
int x;
void dfs(int u,int fa){
	int t=0;
	low[u]=dfn[u]=++cnt;
	for(int i=head[u];i;i=edge[i].nxt){
		int v=edge[i].to;
		if(dfn[v]==0){
			dfs(v,u);
			low[u]=min(low[u],low[v]);
			if(low[v]>=dfn[u]){
				t++;
			}
		}
		else if(fa!=v){
			low[u]=min(low[u],dfn[v]);
		}
	}
	if(t>=1&&u!=x){
		ans[++cmt]=u;
	}
	if(u==x&&t>=2){
		ans[++cmt]=x;
	}
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		int u,v;
		cin>>u>>v;
		addedge(u,v);
		addedge(v,u); 
	}
	for(int i=1;i<=n;i++){
		if(dfn[i]==0){
			x=i;
			dfs(i,i);
		}
	}
	sort(ans+1,ans+cmt+1,cmp);
	cout<<cmt<<endl;
	for(int i=1;i<=cmt;i++){
		cout<<ans[i]<<" ";
	}
	return 0;
} 
posted @ 2022-04-15 20:16  zzzzzz2  阅读(148)  评论(0)    收藏  举报