题目链接
题意:给定一个无向图,求割点的数目。
题解:什么叫割点?简单来说,就是如果删掉一个点,一个图变成了多个图,那么这个点就叫割点。
求割点的方法还是用到了tarjan算法,但是与求强联通分量的tarjan会略有不同。
对于根结点,只要判断其是否有两个及以上的子儿子。
对于非根结点,如果low[v]>=dfn[u],就能判断u是一个割点了。因为你无法将low[v]更新,说明不存在环,因为只有有环存在的情况下才会将整个联通分量的low进行更新。所以就能判断这个点是割点。
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 1e5+100;
struct edge{
int next,v;
}e[maxn*2];
int head[maxn],low[maxn],dfn[maxn],cut[maxn];
int cnt,tot;
void insert(int u,int v){
e[++tot].next=head[u];e[tot].v=v;head[u]=tot;
e[++tot].next=head[v];e[tot].v=u;head[v]=tot;
}
void tarjan(int u,int fa){
dfn[u]=low[u]=++cnt;
int child=0;
for(int i=head[u];i;i=e[i].next){
int v = e[i].v;
if(!dfn[v]){
tarjan(v,fa);
low[u]=min(low[u],low[v]);
if(low[v]>=dfn[u]&&u!=fa) cut[u]=1;
if(u==fa) child++;
}
low[u]=min(low[u],dfn[v]);//一定要是dfn[v],因为v的low值可能在之前被更新了,这样v本来可以是一个割点的,但是low[u]<dfn[v],会造成答案减少。
//具体可以看我下面举的一组样例
}
if(child>=2&&u==fa) cut[u]=1;//如果是根节点,并且有两个儿子 ,那么就是割点
}
int main()
{
int n,m,u,v;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d%d",&u,&v);
insert(u,v);
}
for(int i=1;i<=n;i++){
if(!dfn[i]) tarjan(i,i);
}
int ans=0;
for(int i=1;i<=n;i++) if(cut[i]) ans++;
printf("%d\n",ans);
for(int i=1;i<=n;i++) if(cut[i]) printf("%d ",i);
puts("");
}
/*
4 4
1 2
2 3
3 1
2 4
*/