最近在研究二分图问题,有很多东东还是不熟悉!边学习边完善!
【二分图】图中的顶点可以分为两个不相交的点集u和v,因此,二分图中的每个边都是u中的一个点连接v中的一个点。因为u与v不相交,所以图中不存在环。
【二分图的匹配】给定一个二分图G,M为G边集的一个子集,如果M满足当中的任意两条边都不依附于同一个顶点,则称M是一个匹配。
【二分图的最大匹配】包含边数最多的匹配就是最大匹配。(比如,一个二分图中有三个匹配,即经过求最大匹配算法计算后有三个匹配,那么这个二分图的最大匹配数就是3)
【匈牙利算法】计算最大匹配的一种算法:
算法的理论基础:一个匹配为最大匹配当且仅当匹配中不存在交错路。(所以二分图的求解过程就是一个消除交错路的过程)
交错路:这样的一条路径,在二分图中,它的起点和终点都是都是未匹配的点,它的路径经过的一条连线是一条没被匹配的、一条已经匹配过的,这样交替的出现。也称增广路。
算法过程:将图左边的点逐个与右边的点进行检查,如果右边的点没有匹配过,那么就将它和左边这个点匹配,匹配数加1:如果匹配过但该点存在于一条交错路中(增广路),显然路径里没被匹配的连线比已经匹配了的连线多一条,于是修改匹配图,把路径里所有匹配过的连线去掉匹配关系,把没有匹配的连线变成匹配的,这样匹配数就比原来多1个。不断执行上述操作,直到找不到交错路(增广路径)为止。这时,就得到了最大的匹配数。
PS:
匈牙利算法只需要以每个节点为起点找一次增广路即可求得最大匹配,寻找增广路的复杂度为O(E),总的复杂度为O(VE)
用c/c++语言描述一下匈牙利算法的过程!
程序中涉及的变量说明:n为二分图的左部分顶点个数(顶点标号1-n)、m为二分图的右部分顶点个数(顶点标号为1-m),k为左右顶点的配对关系个数(保存在match二维数组中);
#include<iostream> #include<cstring> #include<cstdio> using namespace std; #define N 150 int visit[N]; int mark[N]; int match[N][N]; int n,m,k; int dfs(int x) { int i; for(i=1;i<=m;i++)//对左边的节点x与右边的节点进行逐一检查 { if(!visit[i]&&match[x][i]) { visit[i]=1;//标记检查过的点 if(!mark[i]||dfs(mark[i]))//如果右边的点没有被匹配或者匹配了但是存在交错路的 { mark[i]=x;//修改匹配关系 return 1; } } } return 0; } int main() { int i,j; while(cin>>n>>m>>k) { memset(mark,0,sizeof(mark)); memset(match,0,sizeof(match)); for(i=1;i<=k;i++) { int a,b; cin>>a>>b; match[a][b]=1;//标记当前匹配关系 } int max=0; for(j=1;j<=n;j++)//对做部分顶点逐个进行遍历 { memset(visit,0,sizeof(visit)); if(dfs(j)) max++; } cout<<max<<endl; } return 0; }
以上,便是匈牙利法求解二分图的最大匹配问题!不足之处,多多交流!
post by heat nan @2013-03-26