二分图匹配

二分图又称作二部图。

定义:设 \(G=(V,E)\) 是一个无向图,如果顶点 \(V\) 可分割为两个互不相交的子集 \((A,B)\),并且图中的每条边 \((i,j)\) 的顶点分别属于 \((A,B)\),则称 \(G\) 为二分图。

显然图 \(G\) 是二分图,当且仅当图中无奇环,因为用 BFS 染色就可以构造子集 \((A,B)\)

"任意两条边没有公共端点" 的边的集合被称为图的一组匹配。在二分图中,包含边数最多的一组匹配被称为二分图的最大匹配。

对于任意一组匹配 \(S\)(\(S\) 是边集), 属于 \(S\) 的边被称为 "匹配边", 不属于 \(S\) 的边被称为 "非匹配边". 匹配边的端点被称为 "匹配点", 其他结点被称为 "匹配点". 如果二分图中存在一条连接两个非匹配点的路径 \(\rm path\), 使得非匹配边与匹配边在 \(\rm path\) 上交错出现,那么称 \(\rm path\) 是匹配 \(s\) 的增广路,也称交错路。

匈牙利算法用来找二分图最大匹配。每次找增广路,显然将匹配边与非匹配边取反能得到更大匹配,于是取反后继续找增广路,至到找不到为止。

Code
#include<bits/stdc++.h>
using namespace std;
#define N 510
int n,m,e;
vector<int> v[N];
int vis[N];
int mat[N];
bool find(int u,int tim){
	if(vis[u]==tim) return 0;
	vis[u]=tim;
	for(auto i:v[u])
		if(mat[i]==0||find(mat[i],tim))
		{
			mat[i]=u;
			return 1;
		}
	return 0;
}
int solve(){
	int ans=0;
	for(int i=1;i<=n;i++)
		ans+=find(i,i);
	return ans;
}
int main()
{
	cin>>n>>m>>e;
	while(e--)
	{
		int a,b;
		cin>>a>>b;
		v[a].push_back(b);
	}
	cout<<solve();
}

本文最重要的任务是证明匈牙利算法的正确性。

  • 必要性,如果匹配 \(M\) 是最大,则无增广路
    反证,如果存在,将增广路取反显然更大

  • 充分性,如果匹配 \(M\) 是无增广路,则匹配 \(M\) 是最大匹配
    假设存在更大匹配 \(M'\), 考虑边集 \(D\),它每一条边只在 \(M\)\(M'\) 出现一次,出现两次不算。

  • 观察 \(D\) 的性质
    1. 每个顶点度数至多为 2,因为每个顶点最多向 \(M\)\(M'\) 各连一条边。
    2. 图中每个联通块,要么是单个点,要么是链,要么是环 (性质 1),且链或环上的边是交替取自 \(M\)\(M'\)(若相邻两边同属一个匹配,它们会共享顶点,不可能)。

  • 考虑 \(M'\)\(M\) 多出来的边,由于 \(|M'|>|M|\) 所以一定存在一条链,两边都属于 \(M'\) 而非全是环。因为环上来自 \(M'\)\(M\) 的边数量一样。
    在原图上考虑这条路径,它的起点与终点都是 \(M\) 的未盖点,同时边交替在于不在 \(M\) 中,这符合增广路的定义。矛盾,命题成立。

posted @ 2026-07-24 19:47  Johnny904903  阅读(7)  评论(0)    收藏  举报