匹配,指一个边集,且其中所有边没有公共顶点。
匈牙利算法能求出二分图上的最大匹配。
之前看大部分讲解里面都说到了增广路,不过有一篇博客在没有说增广路的情况下讲清楚了匈牙利算法
大概是个这样的思路:
假设顶点集是a,b,每次从a上面选择一个元素ai,往b上面找匹配。如果直接找到了一个和ai有边且没匹配过的bj,那么ai,bj匹配,开始ai+1的操作
如果在找匹配的时候,手头上这个元素bj和ai有边,但是已经和另一个元素ak匹配了,那么就递归的对ak再找别的匹配,如果ak实在没法找到别的匹配了,那就换个b的元素。
这个算法会进行递归实现,所有设一个点集vis[]表示本次寻找匹配的时候已经叨扰过哪些b。当ai想要匹配已经和ak匹配的bj时,为了递归的给ak找匹配的时候让ak知道bj已经有点想要了,把vis[j]置1来达到提供这个信息的效果。介于递归实现的尿性,可能会出现“牵一发而动全身”的效果,一堆a的元素都要换匹配(用增广路的说法就是找到了一条很长的增广路),b内可能会有很多元素被置1。另外,因为这是在给一个元素找匹配的时候进行的,所以每次找完了一个点以后都要把vis置空。
以hdu2063为例
题目描述里面是男女配对,所以变量名b指上面的a,gi指上面的b的大小。m指邻接矩阵,g[]用于记录配对情况,used[]指上面的vis
#include <bits/stdc++.h>
using namespace std;
const int maxn=505;
int b,gi,e;
bool used[maxn];
bool m[maxn][maxn];
int g[maxn];
bool found(int x)
{
for(int i=1;i<=gi;i++)
{
if(!used[i]&&m[i][x])
{
used[i]=true;
if(g[i]==0||found(g[i]))
{
g[i]=x;
return true;
}
}
}
return false;
}
int main()
{
while(cin>>e,e!=0)
{cin>>gi>>b;
memset(m,0,sizeof(m));
memset(g,0,sizeof(g));
int c1,c2;
for(int i=1;i<=e;i++)
{
cin>>c1>>c2;
m[c1][c2]=true;
}
int su=0;
for(int i=1;i<=b;i++)
{
memset(used,0,sizeof(used));
if(found(i))su++;
}
cout<<su<<endl;
}
return 0;
}