POJ 3352 无向图边双连通分量,缩点,无重边

为什么写这道题还是因为昨天多校的第二题,是道图论,HDU 4612。

当时拿到题目的时候就知道是道模版题,但是苦于图论太弱。模版都太水,居然找不到。

虽然比赛的时候最后水过了,但是那个模版看的还是一知半解,主要还是对于无向图缩点不了解。

所以今天特意找了道求无向图边双连通分量,然后缩点的题学习一下,这道题的缩点和昨天那道差不多,唯一的区别就是这是无重边的,那题是有重边的。

先搞掉这个,下午把有重边的缩点搞一下。


这里给出一些概念。具体可以到神牛博客看一下。


边连通度:使一个子图不连通的需要删除掉的最小边数,就是该图的边连通度。

桥(割边) :删除某条边时,该图不再连通,那么这条边就是该图的桥(割边)。

边双连通分量:边连通度大于等于2的子图称为边连通分量。


一个边连通分量里面的任意两点,都有2条或者2条以上的路可以互相到达。


这道题的题意,给出N个点M条边,都是无向的。

然后叫你求,最少增加多少条边,可以是的整个图成为一个边双联通分量 。

思路:求出所有的边连通分量,设数量为cnt,然后将一个边连通分量中的点缩成一个块,然后重新建图,这样我们就得到了一棵节点数为cnt ,边数为cnt - 1,的树。

该树上的所有边都是桥。

然后要使得这个图成为一个边连通分量,那么只需将所有的叶子节点连起来即可。

所有最后的答案就是(叶子节点的个数+ 1) / 2。


 

#include <iostream>

#include <cstdio>

#include <algorithm>

#include <string>

#include <cmath>

#include <cstring>

#include <queue>

#include <set>

#include <vector>

#include <stack>

#include <map>

#include <iomanip>

#define PI acos(-1.0)

#define Max 2505

#define inf 1<<28

#define LL(x) ( x << 1 )

#define RR(x) ( x << 1 | 1 )

#define REP(i,s,t) for( int i = ( s ) ; i <= ( t ) ; ++ i )

#define ll long long

#define mem(a,b) memset(a,b,sizeof(a))

#define mp(a,b) make_pair(a,b)

#define PII pair<int,int>

using namespace std;



inline void RD(int &ret) {

    char c;

    do {

        c = getchar();

    } while(c < '0' || c > '9') ;

    ret = c - '0';

    while((c=getchar()) >= '0' && c <= '9')

        ret = ret * 10 + ( c - '0' );

}

int n , m ;

struct kdq{

    int e , next ;

}ed[111111] ,bridge[1111] ,reed[11111] ;

int head[1111] ,num ,rehead[11111] ,renum ;

int low[1111] ,dfn[1111] ;

int st[11111] ;

int fa[1111] ;

bool vis[1111] ;

int dp ; //tarjan的层数

int top ;//栈顶

int bridgenum ;//桥的数量

int cnt ;//缩点后联通块的数量

//可以知道,cnt = bridge + 1

//缩点后,重新建图,所有节点都是一个联通块,所有的边都是桥。故有上述结论。

void init(){

    mem(rehead , -1) ;

    renum = 0 ;

    mem(head , -1) ;

    num = 0 ;

    dp = 0 ;

    top = 0 ;

    bridgenum = 0 ;

    cnt = 0 ;

    mem(low ,0) ;

    mem(dfn ,0) ;

    mem(fa,-1) ;

    mem(vis, 0 ) ;

}

void add(int s ,int e){

    ed[num].e = e ;

    ed[num].next = head[s] ;

    head[s] = num ++ ;

}

void readd(int s ,int e){

    reed[renum].e = e ;

    reed[renum].next = rehead[s] ;

    rehead[s] = renum ++ ;

}

/***模版求无向图的双联通分量,缩点,求出桥(无重边)***/

void tarjan(int now ,int faa){

    dfn[now] = low[now] = dp ++ ;

    st[++ top] = now ;

    for (int i = head[now] ; ~i ;i = ed[i].next ){

        int e = ed[i].e ;

        if(e == faa)continue ;

        if(dfn[e] == 0){

            tarjan(e ,now) ;

            if(low[e] < low[now])low[now] = low[e] ;

            if(low[e] > dfn[now]){

                bridge[bridgenum].e = now ;//桥

                bridge[bridgenum ++].next = e ;

                cnt ++ ;

                do{

                    fa[st[top]] = cnt ;//缩点

                }while(st[top --] != e) ;

            }

        }else if(low[now] > dfn[e])low[now] = dfn[e] ;

    }

}

/***重新建图***/



void rebuild(){

    for (int i = 1 ; i <= n ; i ++ ){

        for (int j = head[i] ; ~j ; j = ed[j].next){

            readd(fa[i], fa[ed[j].e]) ;

            readd(fa[ed[j].e] ,fa[i]) ;

        }

    }

}

int ans = 0 ;

int root = -1 ;

void dfs(int now ,int faa){

    vis[now] = 1 ;

    int sum = 0 ;

    for(int i = rehead[now] ; ~i ;i = reed[i].next){

        int e = reed[i].e ;

        if(e == faa)continue ;

        if(vis[e])continue ;

        sum ++ ;

        dfs(e,now) ;



    }

    if(!sum)ans ++ ;

}

void solve(){

    ans = 0 ;

    rebuild() ;

    dfs(root ,-1) ;

    if(cnt == 1)puts("0") ;

    else

    printf("%d\n",(ans + 1) / 2) ;

}

int main() {

    while(cin >> n >> m){

        init() ;

        while(m -- ){

            int a , b ;

            RD(a) ;RD(b) ;

            add(a , b) ;

            add(b , a) ;

        }



        for (int i = 1 ;i <= n ;i ++ ){//可以处理不连通的图,如果连通的话,这个循环只进行一次。

            if(dfn[i] == 0){

                top = dp = 1 ;

                tarjan(i , -1) ;

                ++ cnt ;

                for (int j = 1 ; j <= n ;j ++ ){//特殊处理顶点的连通块

                    if(dfn[j] && fa[j] == -1)fa[j] = cnt ,root = cnt;

                }

            }

        }

        solve() ;



    }

    return 0 ;

}


 

 

你可能感兴趣的:(poj)