初识二分图最大匹配

初次接触到二分图最大匹配是在http://acm.hdu.edu.cn/showproblem.php?pid=2063

离散数学中有涉及到二分图,完全二分图,匹配,最大匹配及完全匹配的定义,这里就不多做解释。简单介绍一下增广路的概念及其性质。

 设M为二分图G的一个匹配。M中的端点称为M-顶点,其它顶点称为非M-端点。则增广路径:除了起点和终点是非M-顶点,其它路径上所有的点都是M-顶点,且它的边为匹配边和非匹配边交替出现。

增广路径的性质:

  1. 有奇数条边。
  2. 起点在二分图的左半边,终点在右半边。
  3. 路径上的点一定是一个在左半边,一个在右半边,交替出现。(其实二分图的性质就决定了这一点,因为二分图同一边的点之间没有边相连,不要忘记哦。)
  4. 整条路径上没有重复的点。
  5. 起点和终点都是目前还没有配对的点,而其它所有点都是已经配好对的。
  6. 路径上的所有第奇数条边都不在原匹配中,所有第偶数条边都出现在原匹配中。
  7. 最后,也是最重要的一条,把增广路径上的所有第奇数条边加入到原匹配中去,并把增广路径中的所有第偶数条边从原匹配中删除(这个操作称为增广路径的取反),则新的匹配数就比原匹配数增加了1个。

以上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

你可能感兴趣的:(算法)