Input
输入数据的第一行是三个整数K , M , N,分别表示可能的组合数目,女生的人数,男生的人数。0<K<=1000 1<=N 和M<=500.接下来的K行,每行有两个数,分别表示女生Ai愿意和男生Bj做partner。最后一个0结束输入。
Output
对于每组数据,输出一个整数,表示可以坐上过山车的最多组合数。
Sample Input
6 3 3
Sample Output
3
算法的核心就是根据一个初始匹配不停的找增广路,直到没有增广路为止。
匈牙利算法的本质实际上和基于增广路特性的最大流算法还是相似的,只需要注意两点:
(一)每个X节点都最多做一次增广路的起点;
(二)如果一个Y节点已经匹配了,那么增广路到这儿的时候唯一的路径是走到Y节点的匹配点
找增广路的时候既可以采用dfs也可以采用bfs,两者都可以保证O(nm)的复杂度,因为每找一条增广路的复杂度是O(m),而最多增广n次,dfs在实际实现中更加简短。
1、 初始时最大匹配为空
2、 while (找得到增广路径)
3、 do 把增广路径加入到最大匹配中。
如果二分图的左半边一共有n个点,最多找n条增广路径,如果图中有m条边,每一条增广路径把所有边遍历一遍,所以时间复杂度为O(n*m);
算法的思路是不停的找增广轨, 并增加匹配的个数,增广轨顾名思义是指一条可以使匹配数变多的路径,在匹配问题中,增广轨的表现形式是一条"交错轨",也就 是说这条由图的边组成的路径, 它的第一条边是目前还没有参与匹配的,第二条边参与了匹配,第三条边没有..最后一条边没有参与匹配,并且始点和终点还没 有被选择过.这样交错进行,显然 他有奇数条边.那么对于这样一条路径,我们可以将第一条边改为已匹配,第二条边改为未匹配...以此类推.也就是将所有 的边进行"反色",容易发现这样修 改以后,匹配仍然是合法的,但是匹配数增加了一对.另外,单独的一条连接两个未匹配点的边显然也是交错轨.可以证明, 当不能再找到增广轨时,就得到了一个 最大匹配.这也就是匈牙利算法的思路。
二分图最小顶点覆盖 = 二分图最大匹配;
DAG图的最小路径覆盖 = 节点数(n)- 最大匹配数;
二分图最大独立集 = 节点数(n)- 最大匹配数;
源代码:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=1e3+10;
int n1,n2,k;
//n1,n2为二分图的顶点集,其中x属于n1,y属于n2
int map[maxn][maxn],vis[maxn],link[maxn];
//link记录n2中的点y在n1中所匹配的x点的编号
void init()
{
memset(map,0,sizeof map);
memset(link,0,sizeof link);
}
int find(int x)
{
for(int i=1;i<=n2;i++)
{
if(map[x][i]&&!vis[i])//x->i有边,且节点i未被搜索
{
vis[i]=1;//标记节点已经被搜索
//如果i不属于前一个匹配M,或被i匹配到的节点可以寻找到增广路
if(link[i]==0||find(link[i]))
{
link[i]=x;//更新
return 1;//匹配成功
}
}
}
return 0;
}
int main()
{
while(~scanf("%d",&k)&&k){
scanf("%d%d",&n1,&n2);
init();
for(int i=0;i<k;i++)
{
int x,y;
scanf("%d%d",&x,&y);
map[x][y]=1;
}
int ans=0;
for(int i=1;i<=n1;i++)
{
memset(vis,0,sizeof vis);
if(find(i)) ans++;
}
printf("%d\n",ans);
}
return 0;
}