匈牙利算法

本文是结合趣写算法系列之–匈牙利算法的学习笔记。
什么是二分图?
简单来说,如果图中点可以被分为两组,并且使得所有边都跨越组的边界,则这就是一个二分图。准确的说,把一个图的顶点划分为两个不相交集U和V,使得每一条边都分别连接U、V中的顶点。如果存在这样的划分,则此图为一个二分图。
什么是匹配?
在图论中,一个匹配是一个边的集合,其中任意两条边都没有公共顶点。比如下图所示,红边所画的就是一个匹配。其中1、4、5、7称为匹配点,2、3、6、7称为未匹配点,1-5、4-7称为匹配边,其他黑色的边称为未匹配边。
在这里插入图片描述
什么是最大匹配?
一个图所有匹配中,所含匹配边数最多的匹配,称为这个图的最大匹配。

什么是完美匹配?
如果一个图的某一个匹配中,所有的顶点都是匹配点,那么它就是一个完美匹配。显然,完美匹配一定时最大匹配,但每个图并非都存在完美匹配。

那么如果求得最大匹配呢?这时候,我们就切入正题,来讲讲匈牙利算法。
匈牙利算法是基于Hall定理中充分性证明的思想,它是部图匹配最常见的算法,该算法的核心就是寻找增广路径,它是一种用增广路径求二分图最大匹配的算法。
假如有N个男生,M个女生要进行配对,每个人可能对多名异性有好感,如果一对男女互有好感,那么就可以把这一对撮合在一起,举个以下一张关系图的栗子,每一条连线都表示互有好感。
在这里插入图片描述
那么如何用匈牙利算法来实现最大匹配呢?
一:先试着给1号男生找妹子,发现第一个和他相连的1号女生还名花无主,连上一条蓝线。
在这里插入图片描述
二:接着给2号男生找妹子,发现第一个和他相连的2号女生名花无主,连上一条蓝线。
在这里插入图片描述
三:接下来是3号男生,可惜1号女生已经有主了怎么办呢?
我们尝试着给之前1号女生匹配的男生(也就是1号男生)另外分配一个妹子。
(黄色表示这条边被临时拆掉)
在这里插入图片描述
与1号男生相连的第二个女生是2号女生,但是2号女生也有主了,怎么办呢?我们再试着给2号女生的原配重新找个妹子(注意这个步骤和上面是一样的,这是一个递归的过程)
在这里插入图片描述
此时发现2号男生还能找到3号女生,那么之前的问题就迎刃而解了,回溯回去。2号男生找3号女生 1号男生找2号女生 3号男生找1号女生
在这里插入图片描述
最后的结果就是:
在这里插入图片描述
四:接下来是4号男生,可惜按照第三步的节奏没能给4号男生腾出一个妹子来了。

总结:从上面的匈牙利算法的流程来看,其中找妹子就是一个递归的过程,最关键在于一个字,其原则大概是:有机会上,没机会创造机会也要上

#include<stdio.h>
#include<string.h>
#define MAX 100
int n,m;
bool line[MAX][MAX];
bool used[MAX];
int girl[MAX];
bool find(int x)
{
	for(int j=1;j<=m;j++)//扫描每一个妹子 
		if(line[x][j]==true&&used[j]==false)
		//如果有暧昧关系,并且没有被标记过
		{
			used[j]=true;//以便于递归的时候,知道这个妹子被看中过。 
			if(girl[j]==0||find(girl[j]))
			//名花无主或者能挪出个位置来,这里使用递归 
			{
				girl[j]=x;
				return true;
			}
		} 
		return false;
}
int main() 
{
	int cnt=0;
	printf("请分别输入男生和女生的数量:"); 
	scanf("%d %d",&n,&m);
	int t;//有t对暧昧关系 
	printf("请输入有几对暧昧关系:");
	scanf("%d",&t);
	printf("请输入%d对暧昧关系:\n",t);
	int x,y;
	memset(line,false,sizeof(line));
	memset(girl,0,sizeof(girl));//初始化girl没有和任何人配对 
	for(int i=0;i<t;i++)
	{
		scanf("%d %d",&x,&y);
		line[x][y]=true;
	}
	for(int i=1;i<=n;i++)
	{
		memset(used,false,sizeof(used));
		if(find(i))
			cnt++;
	}
	printf("最大匹配数为:%d\n",cnt);
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值