• 割点与桥


    简单的理解:

    割点:在一个连通图中,去掉一点能使图变成多个连通块的点叫割点;

    桥就是去掉一边能使图变成多个联通块的边

     

    求法:

    割点:

    (1)u为树根时,且u有多于一个子树。

    (2)u不为树根,且满足存在 low[v]>=dfn[u]。 则u是割点。

     

    桥:当low[v]>dfn[u]时,边(u,v)、(v,u)就是桥

     

    理解:dfn[u] 为时间戳,就是第几个搜索到点u, low[u]是u或u的子树中通过非父子边(返祖边)追溯到最早的节点。

    当low[v]  >=dfn[u] 时,说明v及v的子树是不能通过返祖边找到比 u更早的边,即点u将  v和v的子树   与 u之前的边隔开。所以u是割点

    low[v]>dfn[u]也是如此。但是 当low[v]==dfn[u]时,v和v的子树时达到u,说明他们是同一个连通分量,所以没有桥。

    代码模板:

    /*
        1.可以求出图的割点和桥
        2.可以求删除每个点后增加的连通块 
    */
    #include<iostream> 
    #include<algorithm>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #define inf 0x3f3f3f3f
    using namespace std;
    typedef long long ll;
    const int maxn=10010;
    const int maxm=200100;
    struct node{
        int u,v,nxt;
        bool cut;//记录是否是桥 
    }e[maxm];
    int h[maxn],cnt;
    int low[maxn],dfn[maxn],st[maxn],tot,top;
    bool instack[maxn];
    bool cut[maxn];//记录是否是割点
    int add_block[maxn];//删除一个点后增加的连通块
    int bridge;//记录桥的条数 
    void add(int u,int v)
    {
        e[cnt].u=u;e[cnt].v=v;e[cnt].cut=0;
        e[cnt].nxt=h[u];h[u]=cnt++;    
    } 
    void tarjan(int u,int pre)
    {
        low[u]=dfn[u]=++tot;
        st[top++]=u;
        instack[u]=1;
        int son=0;
        int pre_cnt=0;//处理重边
        for(int i=h[u];i!=-1;i=e[i].nxt)
        {
            int v=e[i].v;
            if(v==pre&&pre_cnt==0)
            {
                pre_cnt++;
                continue;
            }
            if(!dfn[v])
            {
                son++;
                tarjan(v,u);
                low[u]=min(low[u],low[v]);
                if(low[v]>dfn[u])//
                {
                    bridge++;
                    e[i].cut=1;
                    e[i^1].cut=1;
                }
                if(u!=pre&&low[v]>=dfn[u])//非树根,割点 
                {
                    cut[u]=1;
                    add_block[u]++;
                }
            }
            else 
                low[u]=min(low[u],dfn[v]);
        } 
        if(u==pre&&son>1)    cut[u]=1;//树根,割点 
        if(u==pre)    add_block[u]=son-1;
        instack[u]=0;
        top--;
    }
    int init()//初始化 
    {
        cnt=tot=top=bridge=0;
        memset(h,-1,sizeof(h));
        memset(instack,0,sizeof(instack));
        memset(cut,0,sizeof(cut));
        memset(add_block,0,sizeof(add_block));
        memset(dfn,0,sizeof(dfn));
    }

    题目:poj 1523

    题意:求出割点,并输出删除割点后有几个连通块

    直接带上面代码:

    #include<iostream> 
    #include<algorithm>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #define inf 0x3f3f3f3f
    using namespace std;
    typedef long long ll;
    const int maxn=10010;
    const int maxm=200100;
    struct node{
        int u,v,nxt;
        bool cut;//记录是否是桥 
    }e[maxm];
    int h[maxn],cnt;
    int low[maxn],dfn[maxn],st[maxn],tot,top;
    bool instack[maxn];
    bool cut[maxn];//记录是否是割点
    int add_block[maxn];//删除一个点后增加的连通块
    int bridge;//记录桥的条数 
    void add(int u,int v)
    {
        e[cnt].u=u;e[cnt].v=v;e[cnt].cut=0;
        e[cnt].nxt=h[u];h[u]=cnt++;    
    } 
    void tarjan(int u,int pre)
    {
        low[u]=dfn[u]=++tot;
        st[top++]=u;
        instack[u]=1;
        int son=0;
        int pre_cnt=0;//处理重边
        for(int i=h[u];i!=-1;i=e[i].nxt)
        {
            int v=e[i].v;
            if(v==pre&&pre_cnt==0)
            {
                pre_cnt++;
                continue;
            }
            if(!dfn[v])
            {
                son++;
                tarjan(v,u);
                low[u]=min(low[u],low[v]);
                if(low[v]>dfn[u])//
                {
                    bridge++;
                    e[i].cut=1;
                    e[i^1].cut=1;
                }
                if(u!=pre&&low[v]>=dfn[u])//非树根,割点 
                {
                    cut[u]=1;
                    add_block[u]++;
                }
            }
            else 
                low[u]=min(low[u],dfn[v]);
        } 
        if(u==pre&&son>1)    cut[u]=1;//树根,割点 
        if(u==pre)    add_block[u]=son-1;
        instack[u]=0;
        top--;
    }
    int ans,n,m; 
    int main()
    {
        int a,b;
        int k=0;
        while(scanf("%d",&a)!=EOF)
        {
            cnt=tot=ans=n=0;
            memset(h,-1,sizeof(h));
            memset(add_block,0,sizeof(add_block));
            memset(dfn,0,sizeof(dfn));
            memset(cut,0,sizeof(cut));
            if(!a) break;
            scanf("%d",&b);
            add(a,b);
            add(b,a);
            n=max(n,max(a,b));
            while(scanf("%d",&a)!=EOF)
            {
                if(!a) break;
                scanf("%d",&b);
                add(a,b);
                add(b,a);
                n=max(n,max(a,b));
            }
            tarjan(n,n);
            for(int i=1;i<=n;i++)
                if(cut[i])
                    ans++;
            printf("Network #%d
    ",++k);
            if(!ans)
                printf("  No SPF nodes
    ");
            else
            {
                for(int i=1;i<=n;i++)
                    if(cut[i])
                        printf("  SPF node %d leaves %d subnets
    ",i,add_block[i]+1);
            }
            printf("
    ");
        }
        return 0;
    }

     题目:zoj 2588

    题意: 求有重边无向图中的桥的个数,并输出

    代码:

    /*
        1.可以求出图的割点和桥
        2.可以求删除每个点后增加的连通块 
    */
    #include<iostream> 
    #include<algorithm>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #define inf 0x3f3f3f3f
    using namespace std;
    typedef long long ll;
    const int maxn=10010;
    const int maxm=200100;
    struct node{
        int u,v,nxt;
        bool cut;//记录是否是桥 
    }e[maxm];
    int h[maxn],cnt;
    int low[maxn],dfn[maxn],st[maxn],tot,top;
    bool instack[maxn];
    bool cut[maxn];//记录是否是割点
    int add_block[maxn];//删除一个点后增加的连通块
    int bridge;//记录桥的条数 
    void add(int u,int v)
    {
        e[cnt].u=u;e[cnt].v=v;e[cnt].cut=0;
        e[cnt].nxt=h[u];h[u]=cnt++;    
    } 
    void tarjan(int u,int pre)
    {
        low[u]=dfn[u]=++tot;
        st[top++]=u;
        instack[u]=1;
        int son=0;
        int pre_cnt=0;//处理重边
        for(int i=h[u];i!=-1;i=e[i].nxt)
        {
            int v=e[i].v;
            if(v==pre&&pre_cnt==0)
            {
                pre_cnt++;
                continue;
            }
            if(!dfn[v])
            {
                son++;
                tarjan(v,u);
                low[u]=min(low[u],low[v]);
                if(low[v]>dfn[u])//
                {
                    bridge++;
                    e[i].cut=1;
                    e[i^1].cut=1;
                }
                if(u!=pre&&low[v]>=dfn[u])//非树根,割点 
                {
                    cut[u]=1;
                    add_block[u]++;
                }
            }
            else 
                low[u]=min(low[u],dfn[v]);
        } 
        if(u==pre&&son>1)    cut[u]=1;//树根,割点 
        if(u==pre)    add_block[u]=son-1;
        instack[u]=0;
        top--;
    }
    int init()//初始化 
    {
        cnt=tot=top=bridge=0;
        memset(h,-1,sizeof(h));
        memset(instack,0,sizeof(instack));
        memset(cut,0,sizeof(cut));
        memset(add_block,0,sizeof(add_block));
        memset(dfn,0,sizeof(dfn));
    }
    
    int n,m;
    int ans[maxm];//记录答案 
    int main()
    {
        int t;
        scanf("%d",&t);
        while(t--)
        {
            init();
            scanf("%d%d",&n,&m);
            for(int i=0;i<m;i++)
            {
                int u,v;
                scanf("%d%d",&u,&v);
                add(u,v);
                add(v,u);    
            }    
            for(int i=1;i<=n;i++)
                if(!dfn[i])
                    tarjan(i,i);
            int num=0;
            for(int i=0;i<cnt;i+=2)
            {
                if(e[i].cut)
                    ans[num++]=i/2+1;
            }
            printf("%d
    ",bridge);
            for(int i=0;i<num;i++)
            {
                if(i!=num-1)
                    printf("%d ",ans[i]);
                else
                    printf("%d
    ",ans[i]);    
            }
            if(t) printf("
    ");
        }    
        return 0;    
    } 
  • 相关阅读:
    slf4j中的MDC
    redis incr incrby decr decrby命令
    Java接口响应超时监控
    JDK1.7.0_45源码阅读<java.lang.Boolean>
    Debug JDK变量显形
    Java全角、半角字符的关系以及转换
    模型选择
    经验风险最小化
    支持向量机(下)
    支持向量机(上)
  • 原文地址:https://www.cnblogs.com/xiongtao/p/11221704.html
Copyright © 2020-2023  润新知