浅谈二分图的最大匹配

二分图的最大匹配这个话题,前几期日报都有提过,但讲的不太详细,我在本文详细的说一下。

首先,让我们看一个例子。

昨天,小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;
}

推荐题目:

P3386 【模板】二分图匹配

P1894 【USACO4.2】完美的牛栏The Perfect Stall

本文至此结束,希望能给你带来帮助。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值