• 【点双连通分量+奇环判定】UVA1364 Knights of the Round Table


    UVA1364 Knights of the Round Table

    题意:求无向图(G)上不在任何一个简单奇环上的点的个数。

    分析:首先知道两个定理:

    • 若双连通分量(C)含奇环,则(C)上任意一个点都位于奇环上(不一定是同一个奇环)。

    • 若双连通分量(C)不含奇环,则它一定是个二分图,反之也成立。

      证明(1):设(C)上的奇环为(K),任取两个(K)上的节点(u_1,u_2),由双连通分量性质及奇环条件可知,(u_1,u_2)之间必存在两条点不重复的路径,且一条有偶数个点,另一条有奇数个点。再任意取(C)上另外一个节点(v),则(v)(u_1),(u_2)之间有路径,其中点的个数为偶数的路径与(u_1,u_2)之间奇数个点的路径构成一个奇环,故(v)也在奇环上。

    那么解题思路如下:找出(G)上所有双连通分量(C_i)→判定(C_i)是否为二分图→若(C_i)不是二分图,则将(C_i)上所有点标记为在奇环上→统计没有标记的点的个数,即为答案。

    注意题目中给出图的边是有憎恨关系的边,我们要处理的图(G)是它的补图,即,把所有没有憎恨关系的节点都连上,有憎恨关系的不连。

    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<vector>
    #include<map>
    #include<set>
    #include<queue>
    #include<cstdio>
    #include<stack>
    #define mem(a,n) memset(a,n,sizeof(a))
    #define f(i,a,b) for(int i=a;i<=b;i++)
    #define af(i,a,b) for(int i=a,i>=b;i--)
    #define fe(u,i) for(int i=head[u];i;i=e[i].next)
    
    using namespace std;
    typedef long long LL;
    const int INF = 20010509;
    const int maxn = 1e3 + 100;
    const int maxm = 2e4 + 100;
    
    int dfs_clock;
    int dfn[maxn], low[maxn];
    int head[maxn], cnt = 0;
    int n, m;
    int iscut[maxn], bccno[maxn], bcc_cnt;
    int inoddround[maxn];
    int col[maxn];//二分图判定用
    int hate[maxn][maxn];
    
    struct Edge {
        int from, to, next;
    }e[maxm];
    
    stack<Edge> s;
    //保留在当前BCC中的边
    vector<int> bcc[maxn];
    //记录位于bcc[i]上的所有点
    
    void add(int from, int to, Edge eset[], int head[]) {
        cnt++;
        eset[cnt].from = from;
        eset[cnt].to = to;
        eset[cnt].next = head[from];
        head[from] = cnt;
    }
    
    bool isbinary(int u,int c) {
        col[u] = c;
        for (int i = head[u]; i; i = e[i].next) {
            int v = e[i].to;
            if (bccno[v] != bccno[u]) continue;
          //不属于同一个双连通分量,跳过
            if (col[v] == !col[u]) continue;
            if (col[v] == col[u]) return false;
            if (col[v]==-1 && !isbinary(v, !c)) return false;
        }
        return true;
    }
    
    int dfs(int u, int fa) {
        low[u] = dfn[u] = ++dfs_clock;
        int child = 0;
        for (int i = head[u]; i; i = e[i].next) {
            int v = e[i].to;
            if (!dfn[v]) {
                s.push(e[i]);
                child++;
                low[v] = dfs(v, u);
                low[u] = min(low[u], low[v]);
                if (low[v] >= dfn[u]) {
                    iscut[u] = true;
                    bcc_cnt++; bcc[bcc_cnt].clear();
                    for (;;) {
                        Edge x = s.top(); s.pop();
                        if (bccno[x.from] != bcc_cnt) {
                            bcc[bcc_cnt].push_back(x.from);
                            bccno[x.from] = bcc_cnt;
                        }
                        if (bccno[x.to] != bcc_cnt) {
                            bcc[bcc_cnt].push_back(x.to);
                            bccno[x.to] = bcc_cnt;
                        }
                        if (x.from == u && x.to == v) break;
                    }
                }
            }
            else if (dfn[v] < dfn[u] && v != fa) {
                s.push(e[i]);
                low[u] = min(low[u], dfn[v]);
            }
        }
        if (fa < 0 && child == 1) iscut[u] = false;
        return low[u];
    }
    
    void find_bcc() {
        dfs_clock = bcc_cnt = 0;
        for (int i = 1; i <= n; i++) {
            if (!dfn[i]) dfs(i, -1);
        }
    }
    
    void init() {
        mem(e, 0);
        mem(head, 0);
        cnt = 0;
    
        mem(col, -1);
        mem(inoddround, false);
        mem(hate, 0);
    
        mem(dfn, 0);
        mem(low, 0);
        mem(iscut, 0);
        mem(bccno, 0);
    }
    
    int main(){
        while (cin >> n >> m) {
            if (!n && !m) break;
            init();
            for (int i = 1; i <= m; i++) {
                int u, v;
                cin >> u >> v;
                hate[u][v] = 1;
            }
            for (int i = 1; i <= n; i++) {
                for (int j = i + 1; j <= n; j++) {
                    if (!hate[i][j]) {
                        add(i, j, e, head);
                        add(j, i, e, head);
                    }
                }
            }
    
            find_bcc();
    
            for (int i = 1; i <= bcc_cnt; i++) {
                mem(col, -1);
                //注意不同双连通分量之间可能会有重复的点
                //因此每次在判定二分图之前都要初始化col
                for (int j = 0; j < bcc[i].size(); j++) {
                    bccno[bcc[i][j]] = i;
                    //主要是为了给割点一个bcc编号,这样双连通分量才能一笔画判断二分图
                }
                if (!isbinary(bcc[i][0], 0)) {
                    for (int j = 0; j < bcc[i].size(); j++) {
                        inoddround[bcc[i][j]] = true;
                    }
                }
            }
            int ans = 0;
            for (int i = 1; i <= n; i++) {
                if (!inoddround[i]) ans++;
            }
            cout << ans << endl;
        }
        return 0;
    }
    
  • 相关阅读:
    JDBC与ODBC的区别与应用
    java web项目中classes文件夹下的class和WEB-INF/lib中jar里的class文件加载顺序
    构造方法的继承
    2015-J. PUMA
    阶乘之和 南邮NOJ 1093
    阶乘之和 南邮NOJ 1093
    阶乘之和 南邮NOJ 1093
    阶乘之和 南邮NOJ 1093
    数的计算
    数的计算
  • 原文地址:https://www.cnblogs.com/streamazure/p/13845613.html
Copyright © 2020-2023  润新知