二分图匹配,江湖称二分匹配,图论相关算法。
现在给出两个集合,我们拿约会来举例子。一方是男生集合,一方是女生集合,女生都比较内敛,对不认识的男孩纸并不喜欢一起约会,所以这里边就要有人际关系的问题了。
这里给男生编号n1,n2.....nn;女生编号v1v2....vn;
下面给出女生认识的男生的列表:
v1 :n1 ,n2.
v2 :n2, n3.
v3 : n1.
这里显而易见,1号男生2号男生是比较受欢迎的哈~。不用算法思想的去想这个问题我们可以这样思考:三号女生只认识1号男生,匹配上。(1组搞定)这个时候一号女生就不能选择1号男生了,她只能去选2号男生,这时候2号女生也就有了自己能选择的男生,这里我们就匹配成功了:
v1 n2
v2 n3
v3 n1
这里我们就完成了匹配的过程,这个时候我们因为所有人都有了约会对象,我们这个时候称之为最大匹配,同时也是完美匹配。
最大匹配:一个图所有匹配中,所含匹配边数最多的匹配,称为这个图的最大匹配。
完美匹配:如果一个图的某个匹配中,所有的顶点都是匹配点,那么它就是一个完美匹配。刚刚给出的例子就是完美匹配。
那么我们要如何实现算法呢?因为代码是不能直接看出来如何匹配能得到最大匹配的,所以我们这里就要有一个顺序去寻找最大匹配,这里我们以编号大小的顺序来寻找约会对象。
从v1开始找,先找到了n1.约上,然后是v2,找到了n2,约上。v3找到了n1,但是这里n1和v1已经约好了,怎么办呢?v1对v3说:我还认识n2,我去问问他有没有约会人选,要是没有的话,n1让给你。(其实我想说她是傻逼。。。。)然后v1去找n2,但是n2和v2约上了,这个时候呢v2对v1说:我还认识n3,我去看看他有没有约会的人选,要是没有的话n2,让给你(这两个傻逼。。。。)然后v2找到了n3,n3乐的屁颠屁颠的,说正好没人约我,然后他俩约上了,v2找到了n3,那么v1就能和v2约上了,然后v3也就能和n1约上了,我们这个时候就从刚刚的两组匹配,转到了3组匹配。
刚刚所述的过程,其实就是在找增广路。(这里增广路的含义自己就可以理解了吧~)那么我们如何用代码实现这个过程呢?其实并不难,我们这里需要三个数组,一个是图,一个是询问vis标记,一个是match匹配。
- int map[n][n];
- int vis[n];
- int match[n];
然后就是寻找的过程:
- int find(int x)
- {
- for(int i=0;i<n;i++)
- {
- if(vis[i]==0&&map[x][i]==1)
- {
- vis[i]=1;
- if(match[i]==-1||find(match[i]))
- {
- match[i]=x;
- return 1;
- }
- }
- }
- return 0;
- }
然后整个算法就完美实现了。这里对应杭电例题,希望读者可以独立完成:
杭电2063
过山车
Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 15776 Accepted Submission(s): 6918
Problem Description
RPG girls今天和大家一起去游乐场玩,终于可以坐上梦寐以求的过山车了。可是,过山车的每一排只有两个座位,而且还有条不成文的规矩,就是每个女生必须找个个男生做partner和她同坐。但是,每个女孩都有各自的想法,举个例子把,Rabbit只愿意和XHD或PQK做partner,Grass只愿意和linle或LL做partner,PrincessSnow愿意和水域浪子或伪酷儿做partner。考虑到经费问题,boss刘决定只让找到partner的人去坐过山车,其他的人,嘿嘿,就站在下面看着吧。聪明的Acmer,你可以帮忙算算最多有多少对组合可以坐上过山车吗?
Input
输入数据的第一行是三个整数K , M , N,分别表示可能的组合数目,女生的人数,男生的人数。0<K<=1000
1<=N 和M<=500.接下来的K行,每行有两个数,分别表示女生Ai愿意和男生Bj做partner。最后一个0结束输入。
Output
对于每组数据,输出一个整数,表示可以坐上过山车的最多组合数。
Sample Input
6 3 3
1 1
1 2
1 3
2 1
2 3
3 1
0
Sample Output
这里贴上我的AC完整代码。供新接触这个算法的小伙伴们参考:(提醒,千万把男生女生分清楚了,弄混了就容易WA咯~)
- #include<stdio.h>
- #include<string.h>
- using namespace std;
- const int maxn = 505;
-
- int map[maxn][maxn];
- int vis[maxn];
- int pri[maxn];
- int k,m,n;
- int find(int x)
- {
- for(int i=1;i<=m;i++)
- {
- if(vis[i]==0&&map[i][x])
- {
- vis[i]=1;
- if(pri[i]==-1||find(pri[i]))
- {
- pri[i]=x;
- return 1;
- }
- }
- }
- return 0;
- }
- int main()
- {
- while(~scanf("%d",&k))
- {
- if(k==0)break;
- scanf ("%d%d", &m, &n);
- memset (map, 0, sizeof (map));
- memset (pri, -1, sizeof (pri));
- for(int i=0;i<k;i++)
- {
- int a,b;
- scanf("%d%d",&a,&b);
- map[a][b]=1;
- }
- int output=0;
- for(int i=1;i<=n;i++)
- {
- memset(vis,0,sizeof(vis));
- if(find(i))
- output++;
- }
- printf("%d\n",output);
- }
- }