• 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
    ",(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 ;  
    }  
     

  • 相关阅读:
    numpy基础篇-简单入门教程4
    杭电oj 多项式求和
    杭电oj 素数判定
    杭电oj 水仙花数
    杭电oj 求数列的和
    杭电oj 数值统计
    杭电oj 平方和与立方和
    杭电oj 求奇数的乘积
    杭电 oj 第几天?
    杭电 oj 成绩转换
  • 原文地址:https://www.cnblogs.com/thefirstfeeling/p/4410693.html
Copyright © 2020-2023  润新知