文章目录
二分图的定义
形式化定义
一个二分图 (又称二部图 ,Bipartite Graph)是一个三元组 G = ( U , V , E ) G = (U, V, E) G=(U,V,E),其中:
- U U U 和 V V V 是两个非空 且互不相交 的顶点集合,即 U ∩ V = ∅ U \cap V = \emptyset U∩V=∅
- E E E 是边的集合
- 任意一条边 e ∈ E e \in E e∈E 都连接 U U U 中的一个顶点和 V V V 中的一个顶点
等价定义
染色定义:二分图判定法
图 G G G 是二分图,当且仅当它可以用 2种颜色 对顶点进行染色,使得任意一条边的两个端点颜色不同。
用数学语言表达:存在一个函数 c : V → { 0 , 1 } c: V \rightarrow \{0, 1\} c:V→{0,1},使得对于任意边 ( u , v ) ∈ E (u, v) \in E (u,v)∈E,都有 c ( u ) ≠ c ( v ) c(u) \neq c(v) c(u)=c(v)。
奇环定义:由染色定义衍生而来
图 G G G 是二分图,当且仅当它不包含奇环 (奇数长度的环)。
即:对于任意整数 k k k, G G G 中不存在长度为 2 k + 1 2k + 1 2k+1 的环。
特例:自环、重边和孤立点
- 自环(self-loop)是不允许存在的(因为自环的两个端点在同一个集合中)
- 重边不影响二分图存在
- 孤立点可以属于任意一个集合,不影响二分性
完全二分图
一种特殊的二分图称为完全二分图 ,记作 K m , n K_{m, n} Km,n,其中:
- ∣ U ∣ = m |U| = m ∣U∣=m, ∣ V ∣ = n |V| = n ∣V∣=n
- U U U 中的每个顶点都与 V V V 中的所有顶点相连
- 边数 ∣ E ∣ = m × n |E| = m \times n ∣E∣=m×n

二分图的应用
染色法判定二分图
例题
实现
- 定义一个colors数组记录颜色,对于visited过的节点判断颜色即可。
cpp
int n, m;
vector<list<int>>g(150009);
vector<int>visited(150009, 0);
vector<int>colors(150009, 0);
bool dfs(int x) {
for (auto item : g[x]) {
if (!visited[item]) {
visited[item] = 1;
colors[item] = !colors[x];
if (!dfs(item)) {
return false;
}
}
else {
if (colors[item] == colors[x]) {
return false;
}
}
}
return true;
}
void solve() {
cin >> n >> m;
for (int i = 1; i <= m; i++) {
int u, v;
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
for (int i = 1; i <= n; i++) {
if (!visited[i]) {
visited[i] = 1;
colors[i] = 1;
if (!dfs(i)) {
cout << "No";
return;
}
}
}
cout << "Yes";
}
匈牙利算法
二分图的匹配定义
给定一个二分图 G = ( U , V , E ) G = (U, V, E) G=(U,V,E),图 G G G 的一个匹配(matching) M M M 是边集 E E E 的一个子集,即 M ⊆ E M \subseteq E M⊆E,满足:M M M 中的任意两条边都没有公共顶点。
例题
实现
- 遍历每一个节点每一条边,尝试为每一个节点找一个匹配节点,如果当前匹配节点已经被之前的节点匹配了,就可以尝试为之前的节点再找一个新的匹配节点
- 注意:visited数组不是全局的,当前节点和之前节点共用
cpp
int n1, n2, m, ans;
vector<list<int>>g(509);
int visited[509];// 防止重边u-v
int fa[509];
bool find(int x) {
for (auto item : g[x]) {
if (!visited[item]) {
visited[item] = 1;
if (fa[item]) {
if (find(fa[item])) {
fa[item] = x;
return true;
}
}
else {
fa[item] = x;
return true;
}
}
}
return false;
}
void solve() {
cin >> n1 >> n2 >> m;
for (int i = 1; i <= m; i++) {
int u, v;
cin >> u >> v;
g[u].push_back(v);
}
for (int i = 1; i <= n1; i++) {
memset(visited, 0, sizeof visited);
if (find(i)) {
ans++;
}
}
cout << ans;
}