[双连通分量]poj1679 Road Construction

来源:互联网 发布:淘宝怎么联系卖家客服 编辑:程序博客网 时间:2024/05/17 09:08

题意

给出一个连通的无向图,求至少增加多少条边让这个图变成双连通图

思路

其实题目意思很清晰很明白,但是怎么操作才是重点。
那么首先先想想怎么把整个图简化一下, 先求一次双连通分量。
因为边双连通分量本来就是个边双连通图,那么这里面无论怎样增边,都是无意义的,那么就可以把它缩成一个点。
将所有的边双连通分量都缩成一个点之后,原图变成了一棵树。
那么,如何让一棵树变成一个双连通图,是一个问题。
在网上很容易找到要增加的边就等于叶子的数量加一的和除以二,这里设叶子的数量为nleaves,那么答案就是(nleaves+1)÷2
怎么证明是个问题啊……
其实很容易想到,在一棵树上面,选取到根节点最远的两个叶子节点,然后它们之间连一条边,就可以发现这里产生了一个环,这个环是双连通的。容易发现,如果换成其他的加边的方法, 都没有这种方法产生的环所含的节点多(或一样多),所以这样做一定是最优的。
然后把这个环缩成一个点,然后把这个点作为原图的根节点,然后反复操作,最终得到的答案就是加了(nleaves+1)÷2条边
但是为什么叶子的个数总是为1<-_<-,有待思考<-_<-,或者哪个大神来解答都行<-_<-

代码

关于代码,其实是有些和之前求点双连通分量是不太一样的。
因为可以用更加简单的方式去求。
首先先找到原图中所有的桥,然后桥把原图分成了许多双连通分量,即去掉桥之后,剩下的连通分量刚好对应原图的双连通分量
找到桥之后做dfs,标记点,注意别经过桥就可以了
然后找叶子这一块,其实缩点之后是可以不用考虑的,直接把所属的双连通分量的标号拿来就用
然后统计每一个子节点的度数,因为是无向图嘛,所以总度数为2的点就只连了一条边,也就是叶子节点了。

#include <algorithm>#include <cstring>#include <cstdio>#include <queue>using namespace std;const int MAXN = 5010,          MAXM = 20010;struct Edge {    int u, v, ne;} e[MAXM];int head[MAXN];int n, m;int m_cnt;int bcc_cnt, pre[MAXN], low[MAXN], dfs_clock, bccno[MAXN];bool is_bridge[MAXM];int d[MAXN];int dfs(int u, int fa) {    int lowu = pre[u] = ++dfs_clock;    for(int i = head[u]; ~i; i = e[i].ne) {        int v = e[i].v;        if(!pre[v]) {            int lowv = dfs(v, u);            lowu = min(lowu, lowv);            if(lowv > pre[u]) {                is_bridge[i] = 1;                is_bridge[i ^ 1] = 1;            }        } else {            if(pre[v] < pre[u] && v != fa) {                lowu = min(lowu, pre[v]);            }        }    }    low[u] = lowu;    return lowu;}void dfs2(int u) {    if(bccno[u]) {        return ;    }    bccno[u] = bcc_cnt;    for(int i = head[u]; ~i; i = e[i].ne) {        if(!is_bridge[i]) {            dfs2(e[i].v);        }    }}void find_bcc(int n) {    memset(pre, 0, sizeof pre);    memset(is_bridge, 0, sizeof is_bridge);    dfs_clock = 0;    for(int i = 1; i <= n; ++i) {        if(!pre[i]) {            dfs(i, -1);        }    }    memset(bccno, 0, sizeof bccno);    bcc_cnt = 1;    for(int i = 1; i <= n; ++i) {        if(!bccno[i]) {            dfs2(i);            ++bcc_cnt;        }    }}void AddEdge(int u, int v) {    e[m_cnt].u = u;    e[m_cnt].v = v;    e[m_cnt].ne = head[u];    head[u] = m_cnt++;}void init() {    memset(head, -1, sizeof head);    m_cnt = 0;}int main(void) {    init();    scanf("%d%d", &n, &m);    for(int i = 0; i < m; ++i) {        int u, v;        scanf("%d%d", &u, &v);        AddEdge(u, v);        AddEdge(v, u);    }    find_bcc(n);    for(int i = 1; i <= n; ++i) {        for(int j = head[i]; ~j; j = e[j].ne) {            if(bccno[i] != bccno[e[j].v]) {                d[bccno[i]]++;                d[bccno[e[j].v]]++;            }        }    }    int cnt = 0;    for(int i = 1; i < bcc_cnt; ++i) {        if(d[i] == 2) {            cnt++;        }    }    printf("%d\n", (cnt + 1) >> 1);    return 0;}
原创粉丝点击