二分图的最大匹配这个话题,前几期日报都有提过,但讲的不太详细,我在本文详细的说一下。
首先,让我们看一个例子。
昨天,小K和小L以及他们的朋友跑去游乐场坐过山车,每两人坐一排。每个人都想和一些人一起座,小K被吵得心烦意乱。今天,他决定再次邀请朋友坐过山车,他给出了每人想要一起坐的人,希望你计算最大匹配。
这就是一个经典的二分图最大匹配。
那我们怎么办呢?不急,我们先手动模拟。
再给出的条件中,人们被分成AB组,A组要找B组的人座。也就是这样:
每条边都描述了诸如“X想跟Y座的关系”。
那我们来逐步模拟匹配,首先看A组第一个点的第一条边:(我们用一个match数组记录配对关系,右边那个框就是啦!)(A组的match记录对应的B组成员,B组的match记录对应的A组成员)
我们继续看下面的二号点:
也没啥事,我们接着看三号点:
至此也没啥事,看看关键——四号点:
看!四号点和一号点发生了冲突!那我们应该怎么办呢?其实办法显而易见:你让一号点再找别人匹配不就是了······让我们看看他的第二条边:
结果发现,又何三号点发生了冲突。三号点已经无人可找了,我们只好让一号点再换个人······让我们看看他的第三条边:
这次的变化可大了,首先,一号点找到了B组4,成功匹配。这是,B组1又被抛弃了,我们的四号点立刻就连了上去。然后,所有点都成功匹配,算法结束。
那应该怎么办呢,事实上,流程是这样的:
1.对于A组每一个点,我们对他的每一条边进行遍历。
2.如果这条边连接的B组成员没有被匹配,那么直接连线,记录,退出。
3.如果这条边连接的B组成员被匹配了,**尝试让该B组成员匹配的A组成员找别人!**如果那个A组成员找到了其他人连线,那这个B组成员就被遗弃了,那我们可以立刻连线。
4.如果还是不成功,尝试下一条边。
也就是这个样子啦:
bool dfs(int now)
{
for(now的每条边)
{
// 对方没有和别人连线---------v v----------那个A组成员找到别人连线了
visit[i]=true;
if(visit[该条边连接的B组成员]==false&&(match[该条边连接的B组成员]==0||dfs(match[该条边连接的B组成员]))
{
match[now]=该条边连接的B组成员;
match[该条边连接的B组成员]=now;
return true;//找到人哒哒
}
}
return false;//找不到人
}
好像很简单······
那么,主函数就更简单哒
int main()
{
读入各种数据
int ans=0;
for(int i=1;i<=a_n;i++)
{
memset(visit,0,sizeof(visit));
if(dfs(i))
++ans;
}
cout<<ans;
return 0;
}
存储啥的问题自己搞定啦!
我用的是邻接矩阵,对于B组成员,我直接存在邻接矩阵的a_n+i(i 是该B组成员编号)
完整代码献上:
#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
bool e[401][401];
int match[401];
bool visit[401];
int n,m,k;
bool dfs(int v)
{
visit[v]=true;
for(int i=n+1;i<=m+n;i++)
{
if(e[v][i]==1&visit[i]==false)
{
visit[i]=true;
if(match[i]==0||dfs(match[i]))
{
match[v]=i;
match[i]=v;
return true;
}
}
}
return false;
}
int main()
{
scanf("%d%d%d",&n,&m,&k);
memset(match,0,sizeof(match));
for(int i=1;i<=k;i++)
{
int a,b;
scanf("%d%d",&a,&b);
if(a>n||b>m)
continue;
e[a][b+n]=e[b+n][a]=true;
}
int count=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n+m;j++)
{
visit[j]=false;
}
if(dfs(i))
++count;
}
printf("%d",count);
//system("pause");
return 0;
}
推荐题目:
P1894 【USACO4.2】完美的牛栏The Perfect Stall
本文至此结束,希望能给你带来帮助。