POJ 2942 双连通分量

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=5409

题意:

一些点,一些边。

要求找出那些点,与任何其他任意多个点都不能构成一个简单奇环。

输出这些点的个数。

思路:

好难~

一开始就卡住,想这个怎么去选点都会变成分类讨论。

题解直接取了原图的补图,然后判断哪些点不再奇圈上。

对于一个双连通图,若中间存在奇圈,则可以证明任意两点之间一定存在奇圈,详见大白书P136。所以问题就变成了求哪些点不在这些有奇圈的双连通分量中。对于一个双连通分量含不含有奇圈,只需要判断它是不是二分图、DFS即可。

双连通分量求法与求割顶和桥略有不同,需要用到栈,而且栈里面存的是边。每次找到一个合法情况就一直弹栈把所有在这个双连通分量中的边弹出来。刚开始用栈存点WA了几发,发现存点的话情况会变得极其复杂,还是存边好,因为对于两个点双连通分量,会有点重、但不会有边重的情况。 PS:如果是求边双连通分量,就不需要那么麻烦,因为它连点都不会重。

大白书上的题解会RE哦。

源码:

#include <cstdio>

#include <cstring>

#include <cmath>

#include <cstdlib>

#include <algorithm>

#include <iostream>

#include <utility>

#include <vector>

#include <stack>

using namespace std;

#define gmin(a,b) ((a) < (b) ? (a) : (b))

#define gmax(a,b) ((a) > (b) ? (a) : (b))

const int MAXN = 1000 + 5;

int graph[MAXN][MAXN];

vector<int>lin[MAXN];

int n, m;

int pre[MAXN], low[MAXN], clock;

int bccno[MAXN], bcc_cnt;

int out[MAXN], color[MAXN];

struct Edge

{

    int u, v;

    Edge(){}

    Edge(int _u, int _v){u = _u, v = _v;}

};

stack<Edge>sta;

void Edge_init()

{

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

        for(int j = 1 ; j <= n ; j++)

            graph[i][j] = 1;

    int u, v;

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

        scanf("%d%d", &u, &v);

        graph[u][v] = graph[v][u] = 0;

    }

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

        lin[i].clear();

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

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

            if(i != j && graph[i][j])

                lin[i].push_back(j);

        }

    }

}

int bipartite(int u, int op)

{

    int ans = 1;

    color[u] = op;

    for(int j = 0 ; j < (int)lin[u].size() ; j++){

        int v = lin[u][j];

        if(bccno[v] != bccno[u])    continue;

        if(color[v] == color[u])    return 0;

        if(color[v] == 0)   ans = ans * bipartite(v, -op);

    }

    return ans;

}

void DFS_bcc(int u, int fa)

{

    pre[u] = low[u] = ++clock;

    for(int i = 0 ; i < (int)lin[u].size() ; i++){

        int v = lin[u][i];

//        printf("u = %d, v = %d\n", u, v);

        Edge e = Edge(u,v);

        if(pre[v] == 0){

//            printf("u = %d, v = %d\n", u, v);

            sta.push(e);

            DFS_bcc(v, u);

            low[u] = gmin(low[u], low[v]);

            if(low[v] >= pre[u]){

                ++bcc_cnt;

                while(!sta.empty()){

                    Edge t = sta.top(); sta.pop();

//                    printf("t.u = %d, t.v = %d\n", t.u, t.v);

                    if(bccno[t.u] != bcc_cnt){bccno[t.u] = bcc_cnt;}

                    if(bccno[t.v] != bcc_cnt){bccno[t.v] = bcc_cnt;}

                    if(t.u == u && t.v == v)

                        break;

                }

//                printf("u = %d, bcc_cnt = %d\n", u, bcc_cnt);

                memset(color, 0, sizeof(color));

                if(!bipartite(u, 1)){

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

                        if(bccno[i] == bcc_cnt){

                            out[i] = 1;

                        }

                    }

                }

            }

        }

        else if(pre[v] < pre[u] && v != fa){

            sta.push(e);

            low[u] = gmin(low[u], pre[v]);

        }

    }

}

void BCC()

{

    bcc_cnt = 0;

    memset(pre, 0, sizeof(pre));

    clock = 0;

    while(!sta.empty()) sta.pop();

    memset(bccno, 0, sizeof(bccno));

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

        if(pre[i] == 0){

            DFS_bcc(i, -1);

        }

    }

}

int main()

{

    while(scanf("%d%d", &n, &m) != EOF && n + m){

        Edge_init();

        memset(out, 0, sizeof(out));

        BCC();

        int ans = 0;

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

            if(!out[i])  ans++;

        printf("%d\n", ans);

    }

    return 0;

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值