POJ 3177&& 3352

  题意:添加一定数目的边,构成无向双连通图

  方法:一个有桥的连通图,如何把它通过加边变成边双连通图?方法为首先求出所有的桥,然后删除这些桥边,剩下的每个连通块都是一个双连通子图。把每个双连通子图收缩为一个顶点,再把桥边加回来,最后的这个图一定是一棵树,边连通度为1。

统计出树中度为1的节点的个数,即为叶节点的个数,记为leaf。则至少在树上添加(leaf+1)/2条边,就能使树达到边二连通,所以至少添加的边数就是(leaf+1)/2。具体方法为,首先把两个最近公共祖先最远的两个叶节点之间连接一条边,这样可以把这两个点到祖先的路径上所有点收缩到一起,因为一个形成的环一定是双连通的。然后再找两个最近公共祖先最远的两个叶节点,这样一对一对找完,恰好是(leaf+1)/2次,把所有点收缩到了一起。

  low[]值相同的表示在一个双连通子图中,所有可以利用这个进行缩点。然后统计度为1的结点的数目就是leaf数

渣代码: 

View Code
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>

#define REP(i, n)       for(i = 0; i < n; ++i)
#define FOR(i, L, H)    for(i = L; i <= H; ++i)
#define FORD(i, H, L)   for(i = H; i >= L; --i)
#define CL(arr, val)    memset(arr, val, sizeof(arr))

using namespace std;

const int N = 5024;

struct node {
    int to;
    int next;
} g[N*100];

int head[N];
int dfn[N];
int low[N];
int out[N];
int t, cnt, top, ind;

void init() {
    CL(head, -1); CL(dfn, 0);
    CL(low, 0); CL(out, 0);
    t = cnt = top = ind = 0;
}

void add(int u, int v) {
    g[t].to = v; g[t].next = head[u]; head[u] = t++;
}

void tarjan(int u, int pre) {
    dfn[u] = low[u] = ++ind;
    int i, v;
    bool flag = true;
    for(i = head[u]; i != -1; i = g[i].next) {
        v = g[i].to;
        if(v == pre && flag)    {flag = false; continue;}   //考虑重边
        if(!dfn[v]) {
            tarjan(v, u);
            low[u] = min(low[u], low[v]);
        } else low[u] = min(low[u], dfn[v]);
    }
}

int main() {
    //freopen("data.in", "r", stdin);

    int n, r, i, u, v, ans;
    while(~scanf("%d%d", &n, &r)) {
        init();
        while(r--) {
            scanf("%d%d", &u, &v);
            add(u, v); add(v, u);
        }
        FOR(i, 1, n)    if(!dfn[i]) tarjan(i, -1);
        FOR(u, 1, n) {
            for(i = head[u]; i != -1; i = g[i].next) {
                v = g[i].to;
                if(low[u] != low[v]) {
                    out[low[u]] ++;
                }
            }
        }
        ans = 0;
        FOR(i, 1, ind) {
            if(out[i] == 1) ++ans;
        }
        if(ans == 1)     printf("0\n");
        else    printf("%d\n", (ans + 1)/2);
    }
    return 0;
}

 

 

 

 

 

posted @ 2012-02-12 21:22  AC_Von  阅读(232)  评论(0编辑  收藏  举报