浅谈二分图
二分图匹配
给定一个二分图G,在G的一个子图M中,M的边集E中的任意两条边都不依附于同一个顶点,则称M是一个匹配。
二分图判定
如果一个图是连通的,可以用如下的方法判定是否是二分图:
在图中任选一顶点v,定义其距离标号为0,然后把它的邻接点的距离标号均设为1,接着把所有标号为1的邻接点均标号为2(如果该点未标号的话),如图所示,以此类推。
标号过程可以用一次BFS实现。标号后,所有标号为奇数的点归为X部,标号为偶数的点归为Y部。
接下来,二分图的判定就是依次检查每条边,看两个端点是否是一个在X部,一个在Y部。
如果一个图不连通,则在每个连通块中作判定。代码如下:
bool possibleBipartition(int n, vector<vector<int>>& dislikes) {
vector<vector<int>> mp(n + 1);
for(auto& dislike: dislikes) {
mp[dislike[0]].push_back(dislike[1]);
mp[dislike[1]].push_back(dislike[0]);
}
vector<int> vis(n + 1, -1);
queue<int> que;
for(int x = 1; x <= n; x++) {
if(vis[x] != -1) {
continue;
}
que.push(x);
vis[x] = 0;
while(!que.empty()) {
int curr = que.front();
que.pop();
for(int i = 0; i < mp[curr].size(); i++) {
int nxt = mp[curr][i];
if(vis[nxt] == -1) {
vis[nxt] = (vis[curr] + 1) % 2;
que.push(nxt);
} else if(vis[curr] == vis[nxt]){
return false;
}
}
}
}
return true;
}
匈牙利算法
用增广路求最大匹配(称作匈牙利算法)
算法轮廓:
- 置M为空
- 找出一条增广路径P,通过取反操作获得更大的匹配M’代替M
- 重复2操作直到找不出增广路径为止
代码如下:
bool dfs(int a)
{
for(int i=1;i<=n;i++)
{
if(mp[a][i]==1&&!visit[i])
{
visit[i]=1;
if(link[i]==0||dfs(link[i]))
{
link[i]=a;
return true;
}
}
}
return false;
}
void hungarian()
{
int ans=0;
for(int i=1;i<=m;i++)
{
memset(visit,0,sizeof(visit));
if(dfs(i))
ans++;
}
}