二分匹配的匈牙利算法 51NOD2006(文末)

本文介绍了二分图的概念及其最大匹配问题,并通过匈牙利算法详细解析了如何求解二分图的最大匹配问题。包括算法的基本轮廓、详细步骤及示例代码。

参考及图片来源:https://comzyh.com/blog/archives/148/

二分图:简单来说,如果图中点可以被分为两组,并且使得所有边都跨越组的边界,则这就是一个二分图。准确地说:把一个图的顶点划分为两个不相交集 U  和 V ,使得每一条边都分别连接U 、 V  中的顶点。如果存在这样的划分,则此图为一个二分图。二分图的一个等价定义是:不含有「含奇数条边的环」的图。

(即  如果题目中的 元素 能够 被 分成 两组不同的点集,就是可以用二分图表示的,每个组内的点互不相连,边只在两个点集间产生 )

如下图:

 


最大匹配:一个图所有匹配中,所含匹配边数最多的匹配,称为这个图的最大匹配。下图 是一个最大匹配,它包含 4 条匹配边。

完美匹配:如果一个图的某个匹配中,所有的顶点都是匹配点,那么它就是一个完美匹配。上图  是一个完美匹配。显然,完美匹配一定是最大匹配(完美匹配的任何一个点都已经匹配,添加一条新的匹配边一定会与已有的匹配边冲突)。但并非每个图都存在完美匹配。

求二分图最大匹配可以用最大流(Maximal Flow)  (更复杂) 或者匈牙利算法(Hungarian Algorithm)

下面介绍匈牙利算法:

http://www.nocow.cn/index.php/Translate:USACO/stall4

以此题为例:

算法最基本轮廓:


  1. 置边集M为空(初始化,谁和谁都没连着)
  2. 选择一个新的原点寻找增广路
  3. 重复(2)操作直到找不出增广路径为止(2,3步骤构成一个循环)

模拟步骤如上图所示(过于详细,大牛请无视):

  1. 初始化(清空)
  2. 从A所连接的点中找到一个未在本次循环中搜索过的点2,并将2标记为搜索过,因为2没有被连接过,匹配A2
  3. 结束上次,开始新的循环,将所有点标记为未搜索过
  4. 搜索B,找到一个未在本次循环中搜索过的点2,标记为搜索过
  5. 发现2被匹配过,从2的父亲A寻找增广路,递归搜索A{从A所连接的点中找到一个未在本次循环中搜索过的点5(1已经标记为绿色),将5标记为搜索过,因为5没有被匹配过,匹配A5}找到增广路(此处为增广路的关键
  6. 结束上次,开始新的循环,将所有点标记为未搜索过
  7. 搜索C,找到一个未在本次循环中搜索过的点1,并将1标记为搜索过,发现1未被匹配过,匹配C1
  8. 结束上次,开始新的循环,将所有点标记为未搜索过
  9. 搜索D,找到一个未在本次循环中搜索过的点1,并将1标记为搜索过,发现1被匹配过,递归搜索1的源C寻找增广路
  10. {搜索C,找到一个未在本次循环中搜索过的点5,标记为搜索过,发现5被匹配,进一步返现没有其他可连接点,返回找不到增广路}返回第9步
  11. 搜索D,找到一个未在本次循环中搜索过的点2,发现2被匹配,递归搜索2的源B寻找增广路
  12. {搜索B,找到一个未在本次循环中搜索过的点3,并将3标记为搜索过,发现3未被匹配,匹配B3返回找到}既然B另寻新欢,匹配D2
  13. 结束上次,开始新的循环,将所有点标记为未搜索过,递归搜索D寻找增广路
  14. 搜索E,找到一个未在本次循环中搜索过的点2,并将2标记为搜索过,发现2被匹配过,递归搜索2的源D寻找增广路
  15. {搜索D,发现1,5均被匹配过,返回找不到增广路}
  16. E无其他可连接节点,放弃E,E后无后续节点,已经遍历A-E,结束算法

例题:  51NOD 2006  (看了code就明白)

https://www.51nod.com/onlineJudge/questionCode.html#problemId=2006&noticeId=293729

#include <bits/stdc++.h>
using namespace std;
const int AX = 200;
int g[AX][AX];
int linker[AX];
bool used[AX];
int m,n;

bool dfs( int u ){
	int v;
	for( v = m+1 ; v <= n ;v ++ ){                   
		if( g[u][v] && !used[v] ){                  //如果u 和 v 能够相连 并且 点未被访问
			used[v] = true;				//标记点
			if( linker[v] == -1 || dfs(linker[v]) ){  //如果点没有连接边 或者 已连接 但回溯 到其相连的点发现那个点仍能链接别的点,就将这个点的边变换
				linker[v] = u;   //链接新边
				return true;       //返回真,匹配成功
			}
		}
	}
	return false;         
}

int xyl(){
	int res = 0;
	int u;
	memset(linker,-1,sizeof(linker));      //首先各边初始化为未连接
	for( u = 1 ; u <= m ; u++ ){ 
		memset(used,0,sizeof(used));   //每次都将点设置为为访问过
		if( dfs(u) ) res++;            //用DFS查找匹配
	}
	return res;
}

int main(){
	ios_base::sync_with_stdio(false);
	cin.tie(0);
	cin>>m>>n;
	int x,y;
	while( cin>>x>>y && x != -1 && y != -1 ){
		g[x][y] = 1;                   //加入题目给出的边
	}
	int ans = xyl();                   //调用匈牙利算法
	if( ans == 0  ) printf("No Solution!\n");
	else cout<<ans<<endl;
	return 0;
}


标题基于Python的自主学习系统后端设计与实现AI更换标题第1章引言介绍自主学习系统的研究背景、意义、现状以及本文的研究方法创新。1.1研究背景与意义阐述自主学习系统在教育技术领域的重要性应用价值。1.2国内外研究现状分析国内外在自主学习系统后端技术方面的研究进展。1.3研究方法与创新概述本文采用Python技术栈的设计方法系统创新。第2章相关理论与技术总结自主学习系统后端开发的相关理论技术基础。2.1自主学习系统理论阐述自主学习系统的定义、特征理论基础。2.2Python后端技术栈介绍DjangoFlask等Python后端框架及其适用场景。2.3数据库技术讨论关系型非关系型数据库在系统中的应用方案。第3章系统设计与实现详细介绍自主学习系统后端的设计方案实现过程。3.1系统架构设计提出基于微服务的系统架构设计方案。3.2核心模块设计详细说明用户管理、学习资源管理、进度跟踪等核心模块设计。3.3关键技术实现阐述个性化推荐算法、学习行为分析等关键技术的实现。第4章系统测试与评估对系统进行功能测试性能评估。4.1测试环境与方法介绍测试环境配置采用的测试方法。4.2功能测试结果展示各功能模块的测试结果问题修复情况。4.3性能评估分析分析系统在高并发等场景下的性能表现。第5章结论与展望总结研究成果并提出未来改进方向。5.1研究结论概括系统设计的主要成果技术创新。5.2未来展望指出系统局限性并提出后续优化方向。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值