割点——杨子曰算法

割点——杨子曰算法

瞎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]);

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

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

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

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];
        }
    }
    return 0;
}

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

猜你喜欢

转载自blog.csdn.net/henryyang2018/article/details/80158665