其他分享
首页 > 其他分享> > 并查集判断二分图

并查集判断二分图

作者:互联网

原理

首先,一个图是二分图,当且仅当图中不含奇数环。那么我们只要用并查集判断图中是否含有奇数环。

设二分图的两个集合为s1s2,因为集合内不能有边,那么二分图的环首尾相接的边必然是$ s1\rightarrow s2 \rightarrow s1\rightarrow s2\dots $

image

如图所示,如果边是奇数,不能成环。因为如果起点包含在s1中,经过奇数条边,可以推出起点在s2中,矛盾。

并查集实现

将并查集开为点数的两倍,\(1\sim n\)为集合s1,\(n+1\sim 2n\)为集合s2

将所有边连接的点在并查集中合并。因为是无向图,所以我们将每条边\(u\leftrightarrow v\),将s1us2v合并,s1vs2u合并。即uni(u,v+n), uni(u+n,v);

如果是奇数环,会使某个点pp+n合并在一个集合中。那我们就可以用并查集判断奇数环了。

最后会变成这样:

image

多了个对称的联通块。偶数环的两个联通块是独立的;而奇数环有绿色箭头指向红色联通块,红色箭头指向绿色联通块,也就是6个点全部联通了。

代码

struct DSU { //并查集模板
    vector<int> p;
    DSU(int n) : p(n + 1) { iota(p.begin(), p.end(), 0); }
    int find(int x) { return p[x] == x ? x : p[x] = find(p[x]); }
    void uni(int x, int y) { p[find(x)] = find(y); }
    bool same(int x, int y) { return find(x) == find(y); }
};
//直接存边
bool check(int n, int m) {
	DSU dsu(n * 2);
    for (int i = 1; i <= m; ++i) { //合并所有连接的点
        int u = edge[i].u, v = edge[i].v;
        dsu.uni(u, v + n), dsu.uni(u + n, v);
    }
    for (int i = 1; i <= n; ++i) //判断是否有i与i+n在一个集合中
        if (dsu.same(i, i + n))
            return false;
    return true;
}

标签:二分,判断,奇数,int,s2,s1,查集,find
来源: https://www.cnblogs.com/yHan234/p/16473336.html