原理
首先,一个图是二分图,当且仅当图中不含奇数环。那么我们只要用并查集判断图中是否含有奇数环。
设二分图的两个集合为s1
、s2
,因为集合内不能有边,那么二分图的环首尾相接的边必然是$ s1\rightarrow s2 \rightarrow s1\rightarrow s2\dots $
如图所示,如果边是奇数,不能成环。因为如果起点包含在s1
中,经过奇数条边,可以推出起点在s2
中,矛盾。
并查集实现
将并查集开为点数的两倍,\(1\sim n\)为集合s1
,\(n+1\sim 2n\)为集合s2
。
将所有边连接的点在并查集中合并。因为是无向图,所以我们将每条边\(u\leftrightarrow v\),将s1
的u
与s2
的 v
合并,s1
的v
与s2
的u
合并。即uni(u,v+n), uni(u+n,v);
如果是奇数环,会使某个点p
与p+n
合并在一个集合中。那我们就可以用并查集判断奇数环了。
最后会变成这样:
多了个对称的联通块。偶数环的两个联通块是独立的;而奇数环有绿色箭头指向红色联通块,红色箭头指向绿色联通块,也就是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
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。