割点,桥模板

求割点:

割点:若删掉某点后,原连通图分裂为多个子图,则称该点为割点

low[v]>=pre[u],u为割点。

这说明v的子孙不能够通过其他边到达u的祖先,这样去掉u之后,图必然分裂为两个子图。这样我们处理点u时,首先递归u的子节点v,然后从v回溯至u后,如果发现上述不等式成立,则找到了一个割点u,并且uv的子树构成一个块。


求割边()

割边():删掉它之后,图分裂为两个或两个以上的子图

low[v]>pre[u],(u,v)为割边

但是实际处理时我们并不这样判断,因为有的图上可能有重边,这样不好处理。我们记录每条边的标号(一条无向边拆成的两条有向边标号相同),记录每个点的父亲到它的边的标号,如果边(u,v)v的父亲边,就不能用pre[u]更新low[v]。这样如果遍历完v的所有子节点后,发现low[v]=pre[v],说明u的父亲边(u,v)为割边。


#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
#define maxn 1000
int pre[maxn], iscut[maxn], low[maxn], dfs_clock;
//iscut[i]判断某个点是否为割点
vector<int>G[maxn];
int dfs(int u, int fa)
{
    int lowu=pre[u]=++dfs_clock;
    int child=0;
    for(int i = 0; i < G[u].size(); i++)
    {
        int v = G[u][i];
        if(!pre[v])
        {
            child++;
            int lowv=dfs(v,u);
            lowu=min(lowu,lowv);
            if(lowv>=pre[u])iscut[u]=true;//u不为树根,必须满足lowv>=preu
            //if(lowv>pre[u])则(u, v)满足桥
        }else if(pre[v]<pre[u]&&v!=fa)
            lowu=min(lowu, pre[v]);
    }
    if(fa<0&&child==1)iscut[u]=0;//如果u为树根,那么u必须有多于1棵子树,才能为割点
    low[u]=lowu;
    return lowu;
}
void find_cutpoint(int n)
{
    memset(pre,0,sizeof(pre));
    memset(low,0,sizeof(low));
    memset(iscut,0,sizeof(iscut));
    dfs_clock=0;
    for(int i = 1; i <= n; i++)if(!pre[i])dfs(i,-1);//点从1开始
}
int main()
{
    int n;
    while(~scanf("%d",&n)&&n)
    {
        for(int i = 1; i <= n; i++)G[i].clear();
        int tmp;
        while(scanf("%d",&tmp)&&tmp)
        {
            while(getchar()!='\n')
            {
                int t;
                scanf("%d",&t);
                G[tmp].push_back(t);
                G[t].push_back(tmp);
            }
        }
        find_cutpoint(n);
        int ans = 0;
        for(int i = 1; i <= n; i++)if(iscut[i])ans++;
        printf("%d\n",ans);
    }
    return 0;
}



©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页