割点、桥模板以及点双连通、边双连通

一、概念

概念:

1.桥: 如果在图G中删去一条边e后,图G的连通分支数增加,即W(G-e)>W(G),则称边u为G的桥,又称割边或关节边。

2.割点:如果在图G中删去一个结点u后,图G的连通分枝数增加,即W(G-u)>W(G),则称结点u为G的割点,又称关节点。

3.点双连通分量:不含割点的连通子图

4.边双连通分量:不含割边的连通子图

性质:

1.边双连通分量中,任意两点都在某个边环中。(任意两点不一定在点环中)

2.点双连通分量中,任意两点都在某个点环中。

3.点双连通分量不一定是边双连通分量,边双连通分量不一定是点双连通分量。

二、求桥模板

#define N 100100
#define M 200110

#define INF 0x3fffffff

struct node
{
    int from,to,next;
}edge[2*M];


vector<int>mat[N];//用来建新图.

int n,m;
int savex[M],savey[M];
int cnt,pre[N];
bool mark[2*M];
bool save[2*M];
int low[N];
int mylink[N];

void add_edge(int u,int v)
{
    edge[cnt].to=v;
    edge[cnt].from=u;
    edge[cnt].next=pre[u];
    pre[u]=cnt++;
}

void dfs(int s,int num)//寻找桥
{
    low[s] = num;
    int mi=num;
    for(int p=pre[s];p!=-1;p=edge[p].next)
    {
        int v=edge[p].to;
        if(mark[p]==1) continue;
        if(low[v]==-1)
        {
            mark[p]=1;
            mark[p^1]=1;
            dfs(v,num+1);
            if(low[v]>num) // 说明edge[p]是桥
            {
                save[p]=1;
                save[p^1]=1;
            }
        }
        mi=min(mi,low[v]);
    }
    low[s]=mi;
}

void dfs1(int s,int num)//缩点
{
    mylink[s]=num;
    mark[s]=1;
    for(int p=pre[s];p!=-1;p=edge[p].next)
    {
        int v=edge[p].to;
        if(mark[v]==1||save[p]==1) continue;
        dfs1(v,num);
    }
}

void init()
{
    cnt=0;
    memset(pre,-1,sizeof(pre));
}


int main()
{
    init();
    //构图
    scanf("%d%d",&n,&m);
    for(int i=0;i<m;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        
        savex[i] = x;
        savey[i] = y;
        add_edge(x,y);
        add_edge(y,x);
    }
    
    //tarjan 找桥。 save里面记录了边是否是桥的信息
    memset(low,-1,sizeof(low));
    memset(mark,0,sizeof(mark));
    memset(save,0,sizeof(save));
   
    for(int i=1;i<=n;i++)
    {
        if(low[i]==-1)
            dfs(i,0);
    }
    
    //缩点
    memset(mark,0,sizeof(mark));
    int id=0;
    for(int i=1;i<=n;i++)
    {
        if(mark[i]==0)
        {
            dfs1(i,++id);//这里面还要处理
        }
    }
    
    //每个点对应一个mylink,下列操作构建新图。
    for(int i=0;i<m;i++)
    {
        mat[ mylink[ savex[i] ] ].push_back(mylink[ savey[i] ]);
        mat[ mylink[ savey[i] ] ].push_back(mylink[ savex[i] ]);
    }
    
    return 0;
}

找到桥后,除去桥后的图中连通块即为边双连通分量。

三、割点模板

#define N 110
#define M N*N

struct node
{
    int to,next;
}edge[2*M];

int n;//图中节点个数,边的个数
int pre[N],cnt;
bool cutmark[N];
int vis[N];
int indx;
int mylink[N];
int tn;

void init()
{
    memset(pre,-1,sizeof(pre));
    cnt = 0;
}

void add_edge(int u,int v)//向图中插入一条无向边
{
    edge[cnt].to = v;
    edge[cnt].next = pre[u];
    pre[u] = cnt++;
    edge[cnt].to = u;
    edge[cnt].next = pre[v];
    pre[v] = cnt++;
}

void build_map()//构图
{
    init();
    //然后就是加边
//    char str[10010];
//    getchar();
//    while(gets(str) && str[0]!='0')
//    {
//        stringstream in(str);
//        int tmp;
//        int tu=0;
//        int flag=0;
//        while(in>>tmp)
//        {
//            if(flag == 0)
//            {
//                flag = 1;
//                tu = tmp;
//            }
//            else
//            {
//                add_edge(tu,tmp);
//            }
//        }
//    }
    
}

void dfs(int s,int fa,bool sign)//寻找割点
{
    vis[s] = ++indx;
    int _mi = indx;//这个结点所能到达的最上层
    int _cutedge = 0;
    for (int p=pre[s]; p!=-1; p=edge[p].next) {
        int _v = edge[p].to;
        if(_v == fa) continue;
        if(vis[_v] == -1)
        {
            dfs(_v,s,sign|1);
            if(vis[_v] >= vis[s])
            {
                _cutedge ++;
            }
        }
        _mi = min(_mi,vis[_v]);
    }
    vis[s] = _mi;
    if( (sign == 0&&_cutedge>1)||(sign == 1&&_cutedge>0) )
    {
        cutmark[s] = 1;//为割点
    }
}

void find_cutnode()
{
    memset(cutmark,0,sizeof(cutmark));
    memset(vis,-1,sizeof(vis));
    indx = 0;
    for(int i=1;i<=n;i++)
    {
        if(vis[i] == -1)
        {
            dfs(i,-1,0);
        }
    }
    //是否为割点,存在cutmark中
}

void dfs1(int s,int _id)//缩点用DFS
{
    mylink[s] = _id;
    for(int p=pre[s];p!=-1;p=edge[p].next)
    {
        int _v = edge[p].to;
        if(mylink[_v] == 0 && cutmark[_v] == 0)
        {
            dfs1(_v,_id);
        }
    }
}

void shuodian()//缩点,对应为[1-tn]中的点
{
    tn = 0;
    memset(mylink,0,sizeof(mylink));
    for(int i=1;i<=n;i++)
    {
        if(mylink[i] == 0 && cutmark[i] == 0)
        {
            dfs1(i,++tn);
        }
    }
    //然后把割点也算成一个点双连通块
    for(int i=1;i<=n;i++)
    {
        if(cutmark[i] == 1)
        {
            mylink[i] = ++tn;
        }
    }
    //tn 表示点双连通块的个数.
}

同理,除去割点后,剩余图中的连通块即为点双连通分量。

原文地址:https://www.cnblogs.com/chenhuan001/p/5347877.html