割点
学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;
}