二分图简述
二分图,就是图中可以将点分成两个集合。相同集合的点没有连线,连线仅存在与集合之间的点。
二分图的匹配,就是二分图中相互没有公共点的线的集合,这些线就叫做匹配边,其端点就是匹配点。最大匹配就是二分图匹配中包含线最多的匹配。完美匹配是指一个匹配中所有的点都是匹配点的匹配。
求一个二分图的最大匹配
说二分图的最大匹配之前需要认识几个概念。
1.交替路
是从一个非匹配点开始,经过非匹配边、匹配边的一条路径。
2.增广路
是从一个非匹配点走交替路,途径一个未匹配的点。(如果经过一个未匹配点,交替路就会停在这个未匹配点的位置)这条交替路就是增广路。
增广路有一个特点,就是如果将增广路中的匹配边和非匹配变互换一下,结果仍然是一个匹配,但是匹配边增加了1。
##### 3.匈牙利算法 匈牙利算法主要使用到的特性就是增广路的特点。对于一个二分图的最大匹配,会形成一个匈牙利树,就是树根是一个非匹配点,由根出发每条路都是一个非增广路的交替路。如果一个树存在增广路,则可以使用匹配边和非匹配边互换的方法来消除增广路,从而得到最终的匈牙利树。同时也得到了一个最大匹配。 算法上,一个二分图求匈牙利树的方法主要是对数的遍历的两种方法,DFS和BFS。参考1中由消息的代码和对二分图及其相关知识的详细介绍。 #### 二分图法 hdu 1045
形成一个二分图:
这样就可以使用二分图的方法来求解了。这个思路太巧妙了,有点难以想到。
下面给出了对应的代码:
//转化为二分图
//map 为上面的地图
int a=1 ,b=1,flag = 0;
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
while(map[i][j]=='.'&&j<n) flag=1 , aa[i][j]=a , j++;
if(flag==1) a++ , flag=0;
}
for(int j=0;j<n;j++)
{
while(map[j][i]=='.'&&i<=n) { flag=1 , bb[j][i]=b , j++;}
if(flag==1) b++ ; flag = 0;
}
}
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
if(map[i][j]=='.') cc[aa[i][j]][bb[i][j]] = cc[bb[i][j]][aa[i][j]] = 1;
}
}
//dfs求解最大匹配
//此dfs理解起来还是有点难度,望各位加油
int find_match(int u , int b)
{
for(int i=1;i<=b;i++)
{
if(cc[u][i]==1 && vis[i]==0)
{
vis[i]=1;
if(pp[20+i]==0 || find_match(pp[20+i] , b))
{
pp[20+i] = u; pp[u]=i;
return 1;
}
}
}
return 0;
}
int find_path(int a , int b)
{
int match = 0;
memset(pp,0 ,sizeof(pp));
for(int i=1;i<=a;i++)
{
if(pp[i]==0)
{
memset(vis,0,sizeof(vis));
match+= find_match(i,b);
}
}
return match;
}
此题可以使用,BFS来求解,望博友自行完成,必然有所精进。水平有限,如果博文有错误或者是有更好的方法,请各位看官不吝赐教。
参考
[1]二分图的最大匹配、完美匹配和匈牙利算法
[2]HDU 1045 二分图匹配
[3]原题