割点和桥

 

标题1

一、定义

  连通度(百度百科):

    割点集合:无向图中,删除集合中所有的点以及点所连接的边后,无向图不连通的集合

    割边集合:无向图中,删除集合中所有的边后,无向图不连通的集合

    点连通度:无向图中,割点集合中最小集合中的元素个数

    边连通度:无向图中,割边集合中的最小集合的元素个数

  双联通图,割点,和桥:

    双联通图:无向图中,边连通度或者点连通度大于1

    桥:图的边连通度为1,割边集合中的唯一元素集合中的元素为桥

    割点:图的点连通度为1,割点集合中的唯一元素集合中的元素为桥

  双联通分量

    在图G所有子图中,如果G'是双联通子图,G'不是任何一个双连通子图的真子集,则G'是一个极大双联通子图,特殊的点双联通分量又叫做

  

二、Tagan

  割点的条件:

    (满足一个即可)

    1.如果u树根,那么如果u的子树超过1,则u为割点

    2.如果u不为树根,且满足存在(u,v)为树枝边(u为v在搜索树中的父亲),并使得DFN(u)<=low(v)(删去u以及v以及v的子树不能到达u的祖先)

   

#include<stdio.h>
#include<stdlib.h>
#define FORa(i,s,e) for(int i=s;i<=e;i++)
#define FORs(i,s,e) for(int i=s;i>=e;i--)

using namespace std;

const int N=100,M=200;
int n,m,num_edge,head[N+1],dfn[N+1],low[N+1],index;
bool cut[N+1];
struct Edge{
    int next,to;
}edge[2*M+2]; 
inline void Add_edge(int from,int to){
    edge[++num_edge]=(Edge){head[from],to},head[from]=num_edge;
}
inline int min(int fa,int fb){return fa<fb?fa:fb;}
void Tarjan(int x)
{
    low[x]=dfn[x]=++index;
    int cnt=0;
    for(int i=head[x];i;i=edge[i].next)
    {
        if(!dfn[edge[i].to])
        {
            cnt++;
            Tarjan(edge[i].to),low[x]=min(low[x],low[edge[i].to]);
            if(cnt>1&&x==1||dfn[x]<=low[edge[i].to])    cut[x]=1;
        }    
        else low[x]=min(low[x],dfn[edge[i].to]);
    }
}
int main()
{
    int from,to;
    scanf("%d%d",&n,&m);
    FORa(i,1,m)
    {
        scanf("%d%d",&from,&to);
        Add_edge(from,to),Add_edge(to,from);
    }
    Tarjan(1);
    FORa(i,1,n) printf("%d ",cut[i]);
    return 0;
}
/*7 8
1 5 
1 4
4 5
1 2
1 3
4 3
3 7
3 6*/

  桥的条件:

    (满足一个即可)

    1.如果u树根,那么如果u的子树超过1,则u为割点

    2.如果u不为树根,且满足存在(u,v)为树枝边(u为v在搜索树中的父亲),并使得DFN(u)<=low(v)(删去u以及v以及v的子树不能到达u的祖先)

#include<stdio.h>
#include<stdlib.h>
#define FORa(i,s,e) for(int i=s;i<=e;i++)
#define FORs(i,s,e) for(int i=s;i>=e;i--)

using namespace std;
const int N=1000,M=2000;
int n,m,num_edge=1,top,index,head[N+1],dfn[N+1],low[N+1],fa[N+1];
//注意num_edge是从奇数开始的,因为只有从奇数开始,第一个数就是偶数,那么偶数^1就是比它大1的奇数了 
bool bridge[M+1];
struct Edge{
    int next,to;
}edge[M+1];
inline void Add_edge(int from,int to)
{
    edge[++num_edge]=(Edge){head[from],to},head[from]=num_edge;
}
inline int min(int fa,int fb){return fa<fb?fa:fb;}
inline void Tarjan(int u)
{
    dfn[u]=low[u]=++index;
    for(int i=head[u],v;i;i=edge[i].next)
    {
        v=edge[i].to;
        if(i==(fa[u]^1)) continue;
        if(!dfn[v])
        {
            fa[v]=i,Tarjan(v);
            low[u]=min(low[u],low[v]);
            if(low[v]>dfn[u]) bridge[fa[v]]=bridge[fa[v]^1]=1;
        }
        else low[u]=min(low[u],dfn[v]);                                         
    }
}
int main()
{
    int from,to;
    scanf("%d%d",&n,&m);
    FORa(i,1,m)
    {
        scanf("%d%d",&from,&to);
        Add_edge(from,to),Add_edge(to,from);
    }
    Tarjan(1);
    FORa(i,1,num_edge)
        if(bridge[i])
        {
            printf("%d",edge[i].to);
            printf(" %d\n",edge[i+1].to);
        }
    return 0;
}
/*12
16
12 11
11 8
11 10
8 10
10 9
9 8
9 7
7 6
5 7
6 5
6 4
6 3
4 3
2 3
3 2
4 1*/

 

三、求双联通分量 

  点双联通分量:

     在求割点的时候,每一次找到树枝边和后向边,就将边加入到栈中,如果发现点u是割点,将栈中边(u,v)以上的边都弹出栈。这些边与边的点组成的图就是一个点强连通分量

  边双联同分量     

     在求桥的时候,求出桥(u,v)删去所有桥,剩下的连通块都是一个双联通分量,不包含桥

  将有桥的连通图变成双联通图:

     删去桥。将剩下的连通图缩成一个点,因为每一个连通图是一个边双联通分量,在边双联通图中加边不会减少桥的数目,所以先缩点

      我们可以看如果没有割边,根据树的性质,则将叶子结点连在一起就行,所以要连接的边数为(叶子结点+1)/2

    

 

 

posted @ 2019-07-29 17:50  SeanOcean  阅读(653)  评论(0编辑  收藏  举报