初次接触到二分图最大匹配是在http://acm.hdu.edu.cn/showproblem.php?pid=2063
离散数学中有涉及到二分图,完全二分图,匹配,最大匹配及完全匹配的定义,这里就不多做解释。简单介绍一下增广路的概念及其性质。
设M为二分图G的一个匹配。M中的端点称为M-顶点,其它顶点称为非M-端点。则增广路径:除了起点和终点是非M-顶点,其它路径上所有的点都是M-顶点,且它的边为匹配边和非匹配边交替出现。
增广路径的性质:
以上7条性质是借鉴某大牛的。
了解了增广路的定义以及性质之后,我们仔细理解第7条性质。因为增广路径的长度为奇数,我们不妨设为2*K+1,又因为第一条是非匹配边,且匹配边与非匹配边交替出现,所以非匹配边有K+1条,匹配边有K条。此时,我们做取反操作,则匹配边的个数会在原来的基础上+1。求最大匹配的“匈牙利算法”即是此思想:无论从哪个匹配开始,每一次操作都让匹配数+1,不断扩充,直到找不到增广路径,此时便得到最大匹配。
匈牙利算法的基本模式:
初始时最大匹配为空
while (找得到增广路径)
do 把增广路径加入到最大匹配中。
如果二分图的左半边一共有n个点,最多找n条增广路径,如果图中有m条边,每一条增广路径把所有边遍历一遍,所以时间复杂度为
O(n*m);
从学二分图匹配中较为重要的三个公式:
二分图最小顶点覆盖 = 二分图最大匹配;
DAG图的最小路径覆盖 = 节点数(n)- 最大匹配数;
二分图最大独立集 = 节点数(n)- 最大匹配数;
二分图匹配关键思想在于构图(个人意见哈),能够把一个实际问题通过构图和二分匹配联系起来是比较重要的。
贴一下2063代码:
#include<stdio.h> #include<string.h> #define MAXN 505 int map[MAXN][MAXN],match[MAXN],visited[MAXN]; int DFS(int v,int N) { int i; for(i=1;i<=N;i++){ if(!visited[i]&&map[v][i]){ visited[i]=1; if(DFS(match[i],N)||!match[i]){ match[i]=v; return 1; } } } return 0; } int main() { int K,M,N,i,j; while(scanf("%d%d%d",&K,&M,&N)!=EOF&&K){ memset(map,0,sizeof(map)); int x,y; for(i=1;i<=K;i++){ scanf("%d%d",&x,&y); map[x][y]=1; } memset(match,0,sizeof(match)); for(i=1;i<=M;i++){ memset(visited,0,sizeof(visited)); DFS(i,N); } int num=0; for(i=1;i<=N;i++){ if(match[i]) num++; } printf("%d/n",num); } return 0; }
相关练习:HDU-1068, HDU-1150 ,HDU-1151 ,HDU-1281 ,HDU-1498, HDU-1528 ,HDOJ-1507