二分图
概论
二分图又称作二部图,是图论中的一种特殊模型。 设G = (V,E)是一个无向图,如果顶点V可分割为两个互不相交的子集(A,B),并且图中的每条边(u,v)所关联的两个顶点u和v分别属于这两个不同的顶点集(u->A, v->B),则称图G为一个二分图。
染色法判定二分图
思路
初始将所有节点标记为0,表示未染色;
选取节点u将其染色为1,然后遍历与u相邻的节点v,如果未染色,将其染为3 - 1 = 2,如果染的颜色为1,那么就直接返回False,表示不是二分图。注意,我们需要遍历所有的点,然后染色,如果之前的遍历中染过,那么就无需再染色,否则需要染色,这样做的原因是,可能有多个孤立点或独立的点集!
用bfs较好,防止dfs爆栈,这一点Python多有体会!
当然使用并查集也是可以的,对于节点u,将与它相邻的节点v合并到一个集合!

代码
def bfs():
n = len(g)
color = [0] * n
for i in range(n):
if color[i]:
continue
color[i] = 1
q = deque([i])
while q:
u = q.popleft()
for v in g[u]:
if color[v] == 0:
color[v] = 3 - color[u]
q.append(v)
elif color[v] == color[u]:
return False
return True
题目链接
https://atcoder.jp/contests/abc327/tasks/abc327_d
题目代码
#include<bits/stdc++.h>
using namespace std;
int n,m,t = 1;
void solve()
{
cin >> n >> m;
vector<vector<int>>g(n + 1);
vector<int> a(m + 1),b(m + 1);
for(int i = 1;i <= m;++i) cin >> a[i];
for(int i = 1;i <= m;++i) cin >> b[i];
for(int i = 1;i <= m;++i){
g[a[i]].push_back(b[i]);
g[b[i]].push_back(a[i]);
}
vector<int> color(n + 1,0);
for(int i = 1;i <= n;++i){
if(color[i]) continue;
color[i] = 1;
queue<int> q;
q.push(i);
while(!q.empty()){
int u = q.front();q.pop();
for(int v:g[u]){
if(color[v] == 0){
color[v] = 3 - color[u];
q.push(v);
}else if(color[v] == color[u]){
cout << "No" << '\n';
return;
}
}
}
}
cout << "Yes" << '\n';
}
int main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
// cin >> t;
for(int i = 0;i < t;++i) solve();
return 0;
}
匈牙利算法求二分图的最大匹配
思路
先到先得,能让则让!
时间复杂度:O(mn)

代码
# 匈牙利算法求二分图的最大匹配
from collections import defaultdict
import sys
input = sys.stdin.readline
read = lambda:map(int,input().split())
n1,n2,m = read()
match = [False] * (n2 + 1)
g = defaultdict(list)
for _ in range(m):
u,v = read()
g[u].append(v)
def dfs(u):
for v in g[u]:
if not vis[v]:
vis[v] = True
# 先到先得,能让则让!
if not match[v] or dfs(match[v]):
match[v] = u
return True
return False
res = 0
for u in range(1,n1 + 1):
vis = defaultdict(lambda:False)
if dfs(u):
res += 1
print(res)
例题突破
例题1
https://www.luogu.com.cn/problem/P1129

思路
将每一行的行号与该行黑色块所在的列号相连,加入二分图两边的集合!
任意两行或者任意两列之间的交换都不会破坏他们的平衡性。
如果最大匹配数为n,则表明有解!
例题2

思路
按下凸起部分,相当于删除该凸起点所在的行或列,直到所有的凸起部分都被删除!
我们可以将建立凸起点的行号与列号的匹配,求最小点覆盖集,也就是最大匹配数!
也就是删除最少的点,使得所有边都被删除!
例题3

思路
礼貌拿图【https://zhuanlan.zhihu.com/p/96229700】

将每一个点与周围上下左右四个点建立匹配边,求二分图的最大匹配即可!
注:要进行坐标的映射,将(x,y) -> x * (col - 1) + y
术语
最小点覆盖:找到最少的一些点,使二分图所有的边都至少有一个端点在这些点之中。
一个二分图中的最大匹配数等于这个图中的最小点覆盖数。

浙公网安备 33010602011771号