一文搞懂匈牙利算法【入门版】

         其实匈牙利算法并没有想的这么难和复杂  作为算法应用的话,其实没必要去弄明白运筹学这么复杂和多的定义。

        下面用大家都能懂的大白话阐述一些基本术语:

  1. 二分匹配二分:所有的数只能且刚好个集合(二分);匹配:两集合内部元素互相匹配,条数不限(左集合男A可以喜欢右集合女B‘C’D‘F’H‘L...)【但是绝对不可以在集合内部互相连线,意思是不能男男,不能女女,只能男配女】

  2.  增广路:你只要理解成当前面的都匹配成功后,再来个男孩子想匹配,那就得“操作一下”,一旦dfs递归发现可以把这个男的放进匹配中且匹配数+1,那么意味着这是一条新的增广路,匈牙利算法说白了就是通过统计出现几条新的增广路,得到最大匹配数【想想也好理解,每多一条,自然是因为多了一个匹配成功幸运的男孩子】。
  3. 增广路的算法实现:因为匹配不成功的原因是当前这个男孩子喜欢的女孩子被别的男孩子占了,那么判断当前这个男孩子是否有增广路的思想就很简单:即让每一个当前男孩子now得到他喜欢的女孩子,让pre上一个男孩子换一个,或者抢prepre上上个男孩子的...如此递归结构,最后截至条件就是前面的某一个男孩子找到了他喜欢的但又没人占有的女孩子(举个例子而已,不要较真),那么说明增广路寻找成功;反之则不行。

ps:以上均为自己的容易理解方式,想看专业的可以百度百科


实现主要由三部分组成1.存表,用计算机能懂的语言(邻接表或者邻接矩阵)2.匈牙利 他的核心用递归dfs寻找增广路(其实就是能不能让前面的人再找到新的人)我喜欢单独拿出来写3.主函数

那么基于以上的理解我们再一次通俗理解一下匈牙利
以最通俗的例子说 匈牙利帮你们找对象 
关系就算再复杂 不论是三角恋还是脚踏八条船 但核心只要是男女互配(满足二分匹配条件
即不能男男或者女女(二分匹配:只有左右两集合,集合内部不能有匹配,只能两集合元素互相匹配) 匈牙利都可以瞬间知道最多能配出几对
他的武器用口语来说就是操作(匈牙利,能帮我操作操作不)   用术语就是在遍历中寻找有几条新的增广路
只要匈牙利发现你有增广路,他说:可以帮你操作 那么人数++   最后所有男孩子都遍历完自然就找到了最大匹配

此处用了洛谷P2071的例子(配合使用)

/*
二分匹配之匈牙利 
*/

#include<bits/stdc++.h>
using namespace std;
int n,m,e; //左集合n右集合m一共e个匹配(边) 
int g[510][510];//邻接矩阵存二分匹配(可优化为邻接表)[数据不大可以用稀疏表,大的话就必须要邻接表了] 
int use[510];//对于集合
int result[510];//那个她被谁选走了 

//用来寻找第now个寻求匹配时是否存在增广路 
bool dfs(int now){		//传入当前位置(当前的)now男嘉宾,总体是顺序帮i找 
	for(int i=1;i<=m;i++){		//带他去见所有的她,看看是否相中 
		if(!use[i]&&g[now][i]){		//如果没见过并且本来就互有好感的话 
			use[i]=1;	//先别急着撮合,先标记已经见过了,不然会死循环 
			if(!result[i]||dfs(result[i])){		//如果那个女生并没有人选 那太好直接选 
				result[i]=now;					//如果有了 那么进入dfs寻找增广路,看能不能操作 
				return true;					//相当于把这个女生匹配的男孩子继续去寻找(开始递归)
				// (直到某个男生找到了没人占有他有喜欢的新欢为止) 找得到一路true回来 找不到一路false回来 
			}
		}
	}
	return false;	//全部女生都见过了还不行就返回false失败 
}

//匈牙利算法 
int xyl(){		//其实就是一句话:顺序帮左侧集合找,for i的循环,对每一个i寻找增广路,存在就ans+ 
	int ans=0;
	for(int i=1;i<=n;i++){	//遍历每个人,不然怎么帮他找女友 
		memset(use,0,sizeof(use));	//访问数组每一遍都要清零 因为前一个人看的情况跟后一个人肯定不一样 
		if(dfs(i))ans++;		//寻找增广路的算法是上面的dfs函数 
		//对于每一个i,只要有增广路,说明有操作空间,说明第i个匹配成功,那么最大匹配数ans自然+1 
	}
	return ans;
} 

//主函数 
int main(){
	scanf("%d%d%d",&n,&m,&e);
	//邻接矩阵存所有匹配 
	for(int i=1;i<=e;i++){
		int a,b;
		scanf("%d%d",&a,&b);
		g[a][b]=1;
	}
	printf("%d\n",xyl());
}

进阶版会后期更新 

 本人在校acm选手 喜欢用生动而简洁的话说明白道理。 欢迎大家一起讨论问题

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值