天天看点

无向图 割点模板 (转载)

1.定义:

割点:某个点是割点当且仅当删除该点和与该点相关联的边后图变得不连通。

桥(割边):某条边是割边当且仅当删除该边后图变的不连通。

双连通分支:图G的所有子图G'中,如果G'是连通的,则称G'是双连通子图。如果G'不是任何一个连通子图的真子图,那么图G'是双连通分支。特别的,点双连通分支又叫做块。

2.求割点,桥

对原图进行深度优先搜索,会生成一颗深度优先搜索生成树。定义dfs[u]为u在深度优先搜索生成树中被遍历到的序号,low[u]为u或者他的子树中可以通过非父子边追溯到的最早结点。

那么一个顶点是割点,满足下列条件之一:

1).u是树根,u有两个或两个以上的分支;

2).u不是树根,(u,v)是树边且low[v]>=dfn[u]。

边(u,v)是桥,当且仅当low[v]>dfn[u],(u,v)是树边。

下面是自己总结了多人的模板后写的,自已的模板(我的风格的)

性质:low[]的值相等的点为同一个连通分量。

int root, cnt,son;

//  root记录根下标,son为根直接连接的儿子的数量,当其>1时根为割点。

int vis[Max], dfn[Max], low[Max];  

//  dfn[i]为第i个结点在搜索树中的深度,low[u]定义为u或者u的子树中能够通过非父子边追溯到的最早的节点的DFS开始时间 如下图:红色为low的值,蓝色为dfn的值

bool cut[Max];

// cut[i] = true说明第i个节点为割点。

模板1:
int dep[M],low[M],head[M];
bool cut[M];
int e,n,root,son;
struct E
{
    int to,nxt;
}edge[M*M];

void addedge (int cu,int cv)
{
    edge[e].to = cv;
    edge[e].nxt = head[cu];
    head[cu] = e ++;
}


void dfs (int dep,int u)
{
    dfn[u] = low[u] = dep;
    for (int i = head[u];i != -1;i = edge[i].nxt)
    {
        int v = edge[i].to;
        if (!dfn[v])
        {
            dfs (dep + 1,v);
            if (u == root)
                son ++;
            else
            {
                low[u] = min (low[u],low[v]);
                if (dfn[u] <= low[v])        //.u不是树根,(u,v)是树边且low[v]>=dfs[u] 则为割点
                    cut[u] = true;
            }
        }
        else
            low[u] = min (low[u],dfn[v]);
    }
}
int main ()
{
         .....
         .....
        root = 1,son = 0;
        dfs (1,root);
        int ans = 0;
        if (son > 1)
            ans ++;
        for (int i = 1;i <= n;i ++)
            if (cut[i])
                ans ++;
      .......
}

模板2:


int dfn[M],low[M],head[M],vis[M];
bool cut[M];
int e,n,cnt,root;
struct E
{
    int to,nxt;
}edge[M*M];

void addedge (int cu,int cv)
{
    edge[e].to = cv;
    edge[e].nxt = head[cu];
    head[cu] = e ++;
}

void dfs (int u,int father,int dep)
{
    int son = 0;
    vis[u] = 1;
    dfn[u] = low[u] = dep;
    for (int i = head[u];i != -1;i = edge[i].nxt)
    {
        int v = edge[i].to;
        if (vis[v] == 1&&v != father)
            low[u] = min (low[u],dfn[v]);
        if (vis[v] == 0)
        {
            dfs (v,u,dep + 1);
            son ++;
            low[u] = min (low[u],low[v]);
            if ((u == root && son > 1)||(u != root&&dfn[u] <= low[v]))
                cut[u] = true;
            //if (dfn(u) < low(v))
            //    bridge[k][0] = u;
             //   bridge[k++][1] = v;              // (u,v) 为桥
        }
    }
    vis[u] = 2;
}
int main ()
{
       .......
        root = 1;
        dfs (root,-1,1);
        int ans = 0;
        for (int i = 1;i <= n;i ++)
            if (cut[i])
                ans ++;
    ......
    ......
}      

继续阅读