导读:什么是最大匹配?
要了解匈牙利算法必须先理解下面的概念:
匹配:在图论中,一个「匹配」是一个边的集合,其中任意两条边都没有公共顶点。
最大匹配:一个图所有匹配中,所含匹配边数最多的匹配,称为这个图的最大匹配。
下面是一些补充概念:
完美匹配:如果一个图的某个匹配中,所有的顶点都是匹配点,那么它就是一个完美匹配。
交替路:从一个未匹配点出发,依次经过非匹配边、匹配边、非匹配边…形成的路径叫交替路。
增广路:从一个未匹配点出发,走交替路,如果途径另一个未匹配点(出发的点不算),则这条交替 路称为增广路(agumenting path)。
匈牙利算法
不讲算法证明(我也不会)。
用一个转载的例子来讲解匈牙利算法的流程。
代码实现匈牙利算法
首先是存图模板
//邻接表写法,存稀疏图 int h[N],ne[N],e[N],idx; //n1,n2分别是两个点集的点的个数 int n1,n2,m; void add(int a , int b) { e[idx] = b, ne[idx] = h[a], h[a] = idx++; } void init() { memset(h,-1,sizeof h); } //存边只存一边就行了,虽然是无向图。 for(int i = 0 ; i < n1 ; i ++) { int a,b; cin>>a>>b; add(a,b); }
接下来看算法模板(c++)
//match[j]=a,表示女孩j的现有配对男友是a int match[N]; //st[]数组我称为临时预定数组,st[j]=a表示一轮模拟匹配中,女孩j被男孩a预定了。 int st[N]; //这个函数的作用是用来判断,如果加入x来参与模拟配对,会不会使匹配数增多 int find(int x) { //遍历自己喜欢的女孩 for(int i = h[x] ; i != -1 ;i = ne[i]) { int j = e[i]; if(!st[j])//如果在这一轮模拟匹配中,这个女孩尚未被预定 { st[j] = true;//那x就预定这个女孩了 //如果女孩j没有男朋友,或者她原来的男朋友能够预定其它喜欢的女孩。配对成功,更新match if(!match[j]||find(match[j])) { match[j] = x; return true; }
} } //自己中意的全部都被预定了。配对失败。 return false;
} //记录最大匹配 int res = 0; for(int i = 1; i <= n1 ;i ++) { //因为每次模拟匹配的预定情况都是不一样的所以每轮模拟都要初始化 memset(st,false,sizeof st); if(find(i)) res++; }
下面用一个gif动图来演示这个整个配对的递归过程:
练习例题: 二分图的最大匹配
AC代码
#include #include using namespace std; const int N = 510 , M = 100010; int n1,n2,m; int h[N],ne[M],e[M],idx; bool st[N]; int match[N]; void add(int a , int b) { e[idx] = b, ne[idx] = h[a], h[a] = idx++; } void init() { memset(h,-1,sizeof h); } int find(int x) { //遍历自己喜欢的女孩 for(int i = h[x] ; i != -1 ;i = ne[i]) { int j = e[i]; if(!st[j])//如果在这一轮模拟匹配中,这个女孩尚未被预定 { st[j] = true;//那x就预定这个女孩了 //如果女孩j没有男朋友,或者她原来的男朋友能够预定其它喜欢的女孩。配对成功 if(!match[j]||find(match[j])) { match[j] = x; return true; }
} } //自己中意的全部都被预定了。配对失败。 return false;
} int main() { init(); cin>>n1>>n2>>m; while(m–) { int a,b; cin>>a>>b; add(a,b); }
int res = 0; for(int i = 1; i <= n1 ;i ++) { //因为每次模拟匹配的预定情况都是不一样的所以每轮模拟都要初始化 memset(st,false,sizeof st); if(find(i)) res++; } cout<<res<<endl;
}