[双连通分量]LA3523 Knights of the Round Table

来源:互联网 发布:数据库分析工具 编辑:程序博客网 时间:2024/05/02 02:45

可以说是第一次写蓝书上的题←_←也是第一次去做LA题库上的题,所以是用VJ交的2333。

题意

有很多个骑士,有些骑士相互憎恨,然后可以举办圆桌会议,允许3个及以上的奇数个骑士参加,相互憎恨的骑士不能相邻地坐,问有多少名骑士一个会议都不能参加。

思路

首先先把原来的问题转化成一个图的问题。
把骑士都看做结点,然后建立一个无向图,如果两个骑士可以相邻,也就是不相互憎恨,就连一条无向边。相当于是把憎恨的关系看做边,然后求出原图的补图。
然后题目就会转化为在这个图上,有多少个点不在任何一个简单奇圈上。
那么问题来了,怎么找奇圈呢。显然,因为奇圈是双连通的,所以必然属于原图的某一个双连通分量。
那就把所有的双连通分量都求出来好了。
但是并不是所有的二分图都有用,因为一个二分图必然不存在奇圈,那就找不是二分图的双连通分量。
那么这里已经涉及到两个算法了,一个是tarjan求双连通分量,这个不需要讲。
然后还有一个是判断二分图。二分图的具体概念应该等匈牙利算法时会说,虽然用某流也可以做,但是这里上网看看就好。那么判断很简单,因为是二分图,所以染色判断,相邻的结点必然不会被染成同种颜色,那就做一次dfs就可以了。
然后再看一个不是二分图的双连通分量中怎样去判断某个节点是否在奇圈上。
这里有证明
如图,对于一个简单奇圈C,它属于某一个双连通分量,那么由双连通的性质可以知道,如果它的外面有一个节点也处在这个双连通分量上,则这个节点必然有两条不相同的边连在了C上。
这时候可以发现,通过另外一种办法也可以使这个节点加入到C中,变成另外一个圈,然后它又不是二分图,所以最后可以判定只要不是二分图的双连通图都可以通过某种方法变成一个简单奇圈。
这里写图片描述
所以方法更加简单了,只要不是二分图,就把这个双连通分量上的点都标记一遍,最后一次都没有被标记过的就是答案了。

代码

代码和模板其实不太一样,和蓝书的代码也不太一样,因为蓝书是封装了,然后另外写的,但是如果现求现用,就不需要考虑割点的问题。
每一次找到双连通分量,直接把在这里面的点用栈存起来(懒到不用数组来写),统计一下个数。
然后只有个数大于或者等于三,而且不是个二分图,才把栈里面的取出来标记。

#include <stack>#include <cstdio>#include <cstring&lgt;#include <algorithm&lgt;using namespace std;const int MAXN = 5010,          MAXM = MAXN * MAXN * 2;struct Edge {    int u, v, ne;    Edge() {}    Edge(int in_u, int in_v, int in_ne) {        u = in_u;        v = in_v;        ne = in_ne;    }} e[MAXM];int head[MAXN], m_cnt;void init() {    memset(head, -1, sizeof head);    m_cnt = 0;}void AddEdge(int u, int v) {    e[m_cnt] = Edge(u, v, head[u]);    head[u] = m_cnt++;}stack  S;int bccno[MAXN], bcc_cnt, dfs_clock, pre[MAXN];int color[MAXN];bool bipartite(int u) { // 交叉染色判断二分图     for(int i = head[u]; ~i; i = e[i].ne) {        int v = e[i].v;        if(bccno[v] != bccno[u]) { // 判断是否是在同一个双连通分量上             continue;        }        if(color[v] == color[u]) { // 如果染色相同 则不是二分图             return 0;        }        if(!color[v]) { // 未访问             color[v] = color[u] == 1 ? 2 : 1; // 染色             if(!bipartite(v)) { // 如果剩下未访问的不是二分图 则整个图都不是二分图                 return 0;            }           }     }    return 1;}bool odd[MAXN];stack  S2;bool in_s[MAXN];int low[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]) {            S.push(e[i]);            int lowv = dfs(v, u);            lowu = min(lowu, lowv);            if(lowv >= pre[u]) {                bcc_cnt++;                memset(in_s, 0, sizeof in_s);                while(!S2.empty()) {                    S2.pop();                }                int cnt = 0;                for(;;) {                    Edge x = S.top();                    S.pop();                    if(!in_s[x.u]) {                        S2.push(x.u);                        in_s[x.u] = 1;                        ++cnt;                    }                    if(!in_s[x.v]) {                        S2.push(x.v);                        in_s[x.v] = 1;                        ++cnt;                    }                    bccno[x.u] = bcc_cnt;                    bccno[x.v] = bcc_cnt;                    if(x.u == u && x.v == v) {                        break;                    }                }                 // 找到双连通分量,然后就可以开始标记                memset(color, 0, sizeof color);                color[u] = 1;                if(!bipartite(u) && cnt >= 3) {                    while(!S2.empty()) {                        odd[S2.top()] = 1;                        S2.pop();                    }                }            }        } else {            if(pre[v] < pre[u] && v != fa) {                S.push(e[i]);                lowu = min(lowu, pre[v]);            }        }    }    low[u] = lowu;    return lowu;} void find_bcc(int n) {    memset(pre, 0, sizeof pre);    memset(bccno, 0, sizeof bccno);    dfs_clock = bcc_cnt = 0;    memset(odd, 0, sizeof odd);    for(int i = 1; i <= n; ++i) {        if(!pre[i]) {            dfs(i, -1);        }    }}int A[MAXN][MAXN];int main(void) {    int n, m;    while(~scanf("%d%d", &n, &m) && !(n == 0 && m == 0)) {        init();        memset(A, 0, sizeof A);        for(int i = 0; i < m; ++i) {            int u, v;            scanf("%d%d", &u, &v);            A[u][v] = A[v][u] = 1;        }        for(int u = 1; u <= n; ++u) {            for(int v = u + 1; v <= n; v++) {                if(!A[u][v]) {                    AddEdge(u, v);                    AddEdge(v, u);                }            }        }        find_bcc(n);        int result = 0;        for(int i = 1; i <= n; ++i) {            result += !odd[i];        }        printf("%d\n", result);    }    return 0;}
阅读全文
0 0