看了文章:http://blog.csdn.net/china8848/article/details/2287769 和 http://www.cppblog.com/acronix/archive/2010/08/21/124175.html
http://blog.csdn.net/nomad2/article/details/7422232
其它资料(详细过程):http://www.byvoid.com/blog/hungary/
总结的很好。
算法过程如下:
记录匹配点的数组mx[n]、my[n],其中my下标标识右端的点,my[n]的元素的值标识左端与其相连的点,其值不为-1时表示匹配了(mx同理)。
从左端一个未匹配的点出发,依次遍历右端的点(只是寻找与它相连的点进行操作),如果与其相连并且可以寻找到增广路径(未匹配边 [——匹配边——未匹配边]... 交错),更新匹配信息,并且匹配值加1。
例如下图:
的算法实现测试程序如下:
#include <iostream>
using namespace std;
const int N = 1000;
int nx, ny;
int g[N][N];
int mx[N], my[N];
bool chk[N];
bool searchPath(int u)
{
int v;
for(v = 0; v < ny; v++)
{
if(g[u][v] && !chk[v])
{
chk[v] = true;
if(my[v] == -1 || searchPath(my[v]))
{
my[v] = u;
mx[u] = v;
return true;
}
}
}
return false;
}
int maxMatch()
{
int u, ret = 0;
memset(mx, -1, sizeof(mx));
memset(my, -1, sizeof(my));
for(u = 0; u < nx; u++)
{
if(mx[u] == -1)
{
memset(chk, false, sizeof(chk));
if(searchPath(u))
{
ret++;
}
}
}
return ret;
}
/*
void input()
{
int i;
for(i = 0; i < N; i++)
{
for(int j = 0; j < N; j++)
{
g[i][j] = 0;
}
}
cin >> nx >> ny;
for(i = 0; i < nx; i++)
{
int n, y;
cin >> n;
for(int j = 0; j < n; j++)
{
cin >> y;
g[i][y] = 1;
}
}
cout << endl;
}
void display()
{
int i;
for(i = 0; i < nx; i++)
{
cout << mx[i] << " ";
}
cout << endl;
for(i = 0; i < ny; i++)
{
cout << my[i] << " ";
}
cout << endl;
}
*/
int main()
{
//input();
nx=4;
ny=5;
g[0][2]=1;
g[1][0]=1;
g[1][1]=1;
g[1][1]=1;
g[1][3]=1;
g[2][2]=1;
g[3][2]=1;
g[3][3]=1;
g[3][4]=1;
g[0][3]=1;
int m = maxMatch();
cout << "max match is " << m << endl;
// display();
return 0;
}
寻找增广路径过程:
对于左端的一个点i,如果右端的点j与其相连,如果i、j都是未匹配的点,则直接将两者 (i、j组成的边)加入原匹配并且匹配数加1,如果i是未匹配点,j是已匹配点,则从my[j]点开始寻找(从而达到交错路径的效果),并且如果能找到增广路径,则更新匹配信息,使my[j]=i,mx[i]=j,mx[ my[j] ]=找到的增广路径中从my[j]连接的点(除了j以外的) ;相当于是把j、my[j]边匹配从原匹配中的删除,而把本次增广路径中的奇数边加入到原匹配中。
( 注:若点未匹配,则点所连接的所有边未匹配,若点匹配了该点连接的所有边中只有一条边是匹配了的)
(注:增广路径起始点和终点均为未匹配的点,中间经过的其它点均为已经匹配的,所以找增广路径的条件为右端当前点j未匹配或者 从 与该点匹配的左端点(即my[j])出发能找到增广路径)
例如对应于图中,程序执行过程为:
0出发:右端2与其相连并且未匹配,0-2边为增广路径,加入到原匹配,mx[0]=2,my[2]=0;
1出发:右端1与其相连并且未匹配,1-0边为增广路径,加入到原匹配,mx[1]=0,my[0]=1;
2出发:右端2与其相连,但是2已经匹配左端0,则从左端0开始看是否能找到增广路径,找到0-3时(0-3未匹配并且3是未匹配点,因此截止了)截止。
同时my[2]=2,mx[2]=2。mx[0]=2,my[3]=0。这一步相当于是把边0-2从原匹配中删除,把2-2、0-3加入原匹配。即增广路径中边取反的过程。
(左端未匹配点i - 右端匹配点j(与j匹配的点为my[j])- 左端my[j]开始找)
左端my[j]开始找表示执行与 上行中 括号左端 左端未匹配点i 相同的过程,有点类似递归了。。。
直到找到右端与其相连并且未匹配的点(刚好满足了从左端未匹配点i开始,从右端未匹配点z结束,并且中间穿插的是N(N>=0)对 匹配点j-与j匹配的点my[j] i-j边 (未匹配) ——>...——> 未匹配点z)则表示成功找到了增广路径。
PS:由于在遍历左边的时候,后面的点都完全没有可能参与到匹配中去,所以mx[i]==-1应该是一直成立的,所以代码中的mx数组可以去掉,只需要保留my即可。代码变为:
//二分图最大匹配的匈牙利算法详细请访问:http://blog.csdn.net/china8848
#include <memory.h>
#include <stdio.h>
//分别定义左右最大元素
#define LEFT_MAX 101
#define RIGHT_MAX 301
bool useif[RIGHT_MAX];
//link[]记录与右边元素连接的元素,-1表示没有连接
int link[RIGHT_MAX];
//定义左右两边元素
int left_num,right_num;
//array定义左右两边元素是否有连接
bool array[LEFT_MAX][RIGHT_MAX];
bool can(int t)
{
int i;
for(i=0;i<right_num;i++)
{
if(!useif[i]&&array[t][i])
{
useif[i]=true;
if(link[i]==-1||can(link[i]))
{
link[i]=t;
return true;
}
}
}
return false;
}
int main()
{
int j,i,k,num,count,temp,temp2;
//array,num清零,link清为-1
memset(link,0xFF,sizeof(link));
memset(array,0,sizeof(array));
num=0;
//匹配,num为结果
for(i=0;i<left_num;i++)
{
memset(useif,0,sizeof(useif));
if(can(i))
num++;
}
printf("%d/n",num);
}
return 1;
}