二分图的题有很多不同种的问法,这里先总结一下二分图的最大匹配问题;
求二分图的最大匹配问题我们通常使用的是匈牙利算法;
要理解匈牙利算法的核心思想,就要先弄懂一个概念 —— 增广路
增广路的几个要求:
1 有奇数条边。
2 起点在二分图的左半边,终点在右半边。
3 路径上的点一定是一个在左半边,一个在右半边,交替出现。(其实二分图的性质就决定了这一点,因为二分图同一边的点之间没有边相连,不要忘记哦。)
4 整条路径上没有重复的点。
5 起点和终点都是目前还没有配对的点,而其它所有点都是已经配好对的。
6 路径上的所有第奇数条边都不在原匹配中,所有第偶数条边都出现在原匹配中。
7 最后,也是最重要的一条,把增广路径上的所有第奇数条边加入到原匹配中去,并把增广路径中的所有第偶数条边从原匹配中删除(这个操作称为增广路径的取反),则新的匹配数就比原匹配数增加了1个。
然后给出hdu2063的代码,理解了匈牙利算法的核心要理解程序并不是很难。
/* hdu 2063 过山车*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
using namespace std;
int map[520][520],match[520],vis[520];
int n,m;
int zgfind(int x)
{
for(int i=1;i<=m;i++)
{
if(map[x][i]==1 && vis[i] == 0) //找到存在边并且未被搜索过的点
{
vis[i]=1; //标记节点已经被访问避免重复判断
if( match[i]==0 || zgfind(match[i])==1) //如果i是一个未匹配点或者i点相匹配的点能找到其它匹配点
{
match[i]=x; //生成新的匹配
return 1;
}
}
}
return 0;
}
int main()
{
int k,a,b;
while(scanf("%d",&k)!=EOF&&k)
{
int ans = 0;
memset(map,0,sizeof map);
memset(match,0,sizeof match);
memset(vis,0,sizeof vis);
scanf("%d%d",&n,&m);
while(k--)
{
scanf("%d%d",&a,&b);
map[a][b]=1;
}
for(int i=1;i<=n;i++)
{
memset(vis,0,sizeof vis); //在每次dfs增广路时避免重复访问节点;
if(zgfind(i))
ans++;
}
cout<<ans<<endl;
}
return 0;
}
在查找增广路的函数里实现了二分图左边的点和右边的点的匹配,如果右边的点是一个未被匹配的点,那么左边的点就可以直接与之相匹配,如果右边的点是一个已经被匹配的点,那么查找他的匹配点能否找到一个未被匹配的点作为新的匹配;