先说下二分图的概念吧:二分图是指图可以分成两个点集X和Y,那么任意一条边两个端点分属于不同的点集,也就是说任意一条边的的一个端点在X中,另外一个端点在Y中。并且同一个点集中的任意点之间是没有边相连的,满足这样的性质的图就是二分图。
匹配是指:在给定的一个二分图G中的一个边的子集构成的集合M,M中的任意两条边都没有公共的顶点,那么M就称为一个匹配。那么匹配中含有边数最多的匹配就是最大匹配。
下面来说说匈牙利算法是如何求解二分图的最大匹配的吧。
匈牙利算法是通过不断的找增广路径来扩大匹配集中匹配的边数,我们对左边点集的每一个点找增广路径,直到找不到了增广路径,那么这个时候的匹配就是最大匹配。
匈牙利算法的基本思想就是这样,如果想要具体了解,自己画个图理解下就行了,很容易理解的。下面我们来说说匈牙利算法是如何实现的吧。
首先我们需要构建一个二分图,这个随你。之后为了找增广路径,我们需要一个match[i]表示右边点集的i的匹配的左边的点,如果是-1,那么表示还没有找到匹配的点;这个数组在我们找增广路径的时候非常重要。
其次我们需要一个vis数组,用来表示在每一次找增广路径的时候左边的点是否被访问过,这个是为了防止重复找同一个点而出现错误。
我们再找增广路径的时候,如果一个点的match[i]==-1,那么我们就找到了一条增广路径,返回1;否则我们继续找,对match[i]进行dfs,如果最终都没有找到增广路径,那么就只好返回零表示没有找到增广路径。
基于这个思想,我把在POJ上的2239那道题的代码贴上来,匈牙利算法基本都是差不多的,有需要的可以拿过去参考一下。呵呵——
- #include <stdio.h>
- #include <string.h>
- #include <stdlib.h>
- #define LMAX 310
- #define RMAX 90
- int edge[LMAX][RMAX];
- int vis[RMAX],match[RMAX];
- int RN;//左边的顶点的个数,右边顶点的个数
- int dfs(int i)
- {
- int j;
- for(j=1;j<=RN;j++)
- {
- if(edge[i][j]&&vis[j]==0)
- {
- vis[j]=1;
- if(match[j]==-1)
- {
- match[j]=i;
- return 1;
- }
- else
- {
- if(dfs(match[j]))
- {
- match[j]=i;
- return 1;
- }
- }
- }
- }
- return 0;
- }
- int solve(int LN)
- {
- memset(match,-1,sizeof(match));
- int i,sum=0;
- for(i=1;i<=LN;i++)
- {
- memset(vis,0,sizeof(vis));
- if(dfs(i))
- sum++;
- }
- return sum;
- }
- int main()
- {
- int n,t;
- int i;
- int a,b;
- RN=7*12;
- // freopen("data.txt","r",stdin);
- while(scanf("%d",&n)!=EOF)
- {
- memset(edge,0,sizeof(edge));
- for(i=1;i<=n;i++)
- {
- scanf("%d",&t);
- while(t--)
- {
- scanf("%d %d",&a,&b);
- edge[i][(a-1)*12+b]=1;
- }
- }
- printf("%d\n",solve(n));
- }
- return 0;
- }