图的割点

传送门:https://www.luogu.org/problem/P3388

还有两点要注意的

1.割点可能有很多个

2.无向图和有向图都有割点

然后我们来看一看如何实现割点

方案一:枚举去掉每个点的情况,DFS判断时间复杂度O(n^2)

方案二:Tarjan实现

同样是用dfn表示搜索次序,但是low表示可以最早回溯到的祖先的dfn值

之后就和tarjan没有太多不同了

#include<bits/stdc++.h>
using namespace std;
int n,m;
struct edge{
    int next,to;
}e[200009];int tot;
int first[100009];
int dfn[100009];
int low[100009];
int js;
int ans;
bool book[100009];
inline void add_edge(int a,int b)
{
    e[++tot].to=b;
    e[tot].next=first[a];
    first[a]=tot;
}
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    return x*f;
}
void tarjan(int now,int fa)//now是我在那里,fa是我的爸爸是谁
{
    js++;
    dfn[now]=js,low[now]=js;
    int child=0;//记录有几个子树
    for(int i=first[now];i;i=e[i].next)
    {
        if(dfn[e[i].to]==0)
        {
            tarjan(e[i].to,now);
            if(low[now]>low[e[i].to])low[now]=low[e[i].to];
            if(now==fa)child++;//如果是根结点就+1
            if(now!=fa&&low[e[i].to]>=dfn[now])book[now]=true;//如果不是根结点并且它的子节点不通过它就无法回到之前的节点,那么说明它就是割点
        }
        else if(e[i].to!=fa&&dfn[e[i].to]<=low[now])
        {
            low[now]=dfn[e[i].to];
        }
    }
    if(now==fa&&child>=2)book[now]=true;//如果是根结点,并且儿子数量有两个以上那么明显就是割点
    return;
}
int main() {
    n=read(),m=read();
    for(register int i=1;i<=m;i++)
    {
        int a=read(),b=read();
        add_edge(a,b);
        add_edge(b,a);
    }
    for(int i=1;i<=n;i++)
    {
        if(dfn[i]==0)
        {
            tarjan(i,i);//因为割点有多个,并且图可能本身不连通,所以多次枚举
        }
    }
    for(int i=1;i<=n;i++)
    {
        if(book[i]==true)ans++;
    }
    cout<<ans<<endl;
    for(int i=1;i<=n;i++)
    {
        if(book[i]==true)
        {
            cout<<i<<" ";
        }
    }
    return 0;
}

 

posted @ 2019-10-20 15:46  nono_ttaa  阅读(176)  评论(0编辑  收藏  举报