POJ--3352(边双连通分量,tarjan)

2015-05-06 23:12:52

题目:给出无向图,让你求出至少要加多少条边才能使之成为边双连通图。

思路:按照大白书的思路打的... 首先通过简易 tarjan 求出所有桥,标记一下。然后 dfs 跑图,不经过桥,从而找出所有边双连通分量。

  将所有边双连通分量缩点后,加上桥,就形成一棵树,那么所要加的边数就是 (叶子数 + 1)/ 2。

  【简证:对于一对叶子a,b,在它们之间建边可以形成一个环:a -> b -> lca -> a,即达成边双连通。

       如果处理所有的叶子对,那么整个图就是环与环的叠加、邻接,显然双连通。

       为了处理所有叶子,至少加边:(叶子数+1)/ 2】

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;

#define getmid(l,r) ((l) + ((r) - (l)) / 2)
#define MP(a,b) make_pair(a,b)
#define PB(a) push_back(a)

typedef long long ll;
typedef pair<int,int> pii;
const double eps = 1e-8;
const int INF = (1 << 30) - 1;
const int MAXN = 1010;

int first[MAXN],ecnt;
int n,m,bg[MAXN << 1],deg[MAXN];
int dfn[MAXN],low[MAXN],bcnt,bcc[MAXN],tot;

struct edge{
    int v,next;
}e[MAXN << 1];

void Init(){
    memset(deg,0,sizeof(deg));
    memset(bg,0,sizeof(bg));
    memset(first,-1,sizeof(first));
    ecnt = bcnt = tot =  0;
}

void add_edge(int u,int v){
    e[ecnt].next = first[u];
    e[ecnt].v = v;
    first[u] = ecnt++;
}

void Dfs(int p,int pre){ //find bridge
    dfn[p] = low[p] = ++tot;
    for(int i = first[p]; ~i; i = e[i].next){
        int v = e[i].v;
        if(v == pre) continue;
        if(!dfn[v]){
            Dfs(v,p);
            low[p] = min(low[p],low[v]);
            if(low[v] > dfn[p]) bg[i] = 1;
        }
        else low[p] = min(low[p],dfn[v]);
    }
}

void Tarjan(){
    memset(low,0,sizeof(low));
    memset(dfn,0,sizeof(dfn));
    for(int i = 1; i <= n; ++i)
        if(!dfn[i]) Dfs(i,0);
}

void Dfs2(int p,int pre){
    bcc[p] = bcnt;
    for(int i = first[p]; ~i; i = e[i].next){
        int v = e[i].v;
        if(bcc[v] || bg[i]) continue;
        Dfs2(v,p);
    }
}

void BCC(){
    memset(bcc,0,sizeof(bcc));
    for(int i = 1; i <= n; ++i) if(!bcc[i]){
        ++bcnt;
        Dfs2(i,0);
    }
}

int main(){
    int a,b;
    while(scanf("%d%d",&n,&m) != EOF){
        Init();
        for(int i = 1; i <= m; ++i){
            scanf("%d%d",&a,&b);
            add_edge(a,b);
            add_edge(b,a);
        }
        Tarjan();
        BCC();
        for(int i = 1; i <= n; ++i){
            for(int j = first[i]; ~j; j = e[j].next){
                int v = e[j].v;
                if(bcc[i] != bcc[v]) deg[bcc[v]]++;
            }
        }
        int cnt = 0;
        for(int i = 1; i <= bcnt; ++i) if(deg[i] == 1) ++cnt;
        printf("%d\n",(cnt + 1) / 2);
    }
    return 0;
}

 

posted @ 2015-05-06 23:21  Naturain  阅读(132)  评论(0编辑  收藏  举报