匈牙利算法 -- 匹配问题

假期 2020.01.27

匈牙利算法(Hungarian algorithm)介绍

核心:寻找增广路径,用增广路径求二分图最大匹配的算法
二分图: 二分图又称作二部图,是图论中的一种特殊模型。 设G=(V,E)是一个无向图,如果顶点V可分割为两个互不相交的子集(A,B),并且图中的每条边(i,j)所关联的两个顶点i和j分别属于这两个不同的顶点集(i in A,j in B),则称图G为一个二分图。(该定义来自百度百科
在这里插入图片描述
(图片来源于百度图片
当子图中没有任何一个同时连接Xi和Yj的同一顶点的时候我们称为二部图的一个匹配。如顶点集V可分割为两个互不相交的子集{xi}与{yi} ,选择这样的子集中边数最大的子集称为图的最大匹配问题(maximal matching problem)。
与匹配相关定义
完全匹配:如果一个匹配中,|x| <= |y| 且匹配数等于|x| ,则称此匹配为完全匹配;特别的当 |x| = |y|时称为完美匹配。
常见实现步骤:

  • 寻找增广路径,若P是图G中一条连通两个未匹配顶点的路径,并且属于M的边和不属于M的边(即已匹配和待匹配的边)在P上交替出现,则称P为相对于M的一条增广路径。表型形式是一条交错路径,这条由边组成的路径,它的第一条边没有参与匹配,第二条边参与匹配,第三条边没有参与匹配,最后一条边也没有参与匹配,并且始点和终点没有匹配。另外,单独的一条连接两个未匹配点的也显然是交错路径。
  • 在步骤一的情况,下增加匹配的个数。
  • 当找不到增广路径时,即找到了最大匹配,结束程序。

用途:主要用于解决一些与二分图匹配有关的问题,即部图匹配最常见的算法。


题目描述

某公司存在分工问题,一定数量的女员工与男员工搭配,如何搭配才能实现人员的最大利益,即最大匹配问题。


思路分析

见匈牙利算法介绍


算法分析

  1. 构建网络,创建邻接表
  2. 初始化所有的顶点未访问
  3. 依次检查u的邻接点v,如果v未被访问,则将v设置为被访问,并且判断v是否已经匹配,若没有,则u,v匹配,否则从v的邻接点出发,检查是否有增广路径,如果有,就沿增广路径反色,再u v匹配,否则返回0
  4. 当找不到增广路径时,即找到了最大匹配,结束程序

代码解析

#include<algorithm>
#include<cstring>
#include <iostream>
#include<queue>
using namespace std;
#define Max_size 100
struct Vertex {
	int first;
}vertex[Max_size];
struct Edge {
	int start;
	int next;
}edge[Max_size];
int match[Max_size],visited[Max_size];//匹配数组,访问数组
int current_num = 0,num = 0;
void add_edge(int u, int v);;
void add(int u, int v);
void Print(int total);
void Print_flow(int total);
int maxmatch(int i);
int main()
{
	int male_num, female_num, ple_total = 0, u, v;//男性数量,女性数量
	cout << "请输入male_num与female_num: ";
	cin >> male_num >> female_num;
	ple_total = female_num + male_num;//总人数
	memset(vertex, -1, sizeof(vertex));//初始化
	current_num = 0;
	cout << "请输入可以匹配的员工编(以 0 0 结束程序输入):" << endl;
	while (cin >> u >> v, u + v != 0)
		add(u, v);
	cout << endl;
	Print(ple_total);//总顶点数,输出最初邻接网络
	for (int i = 1; i <= female_num; i++)
	{
		memset(visited, 0, sizeof(visited));
		if (maxmatch(i))
			num++;
	}
	cout << "最大匹配是:" << num << endl;
	Print(ple_total);//输出最终邻接网络
	Print_flow(female_num);//输出最终方案
	return 0;
}
void add(int u, int v)
{
	add_edge(u, v);
	add_edge(v, u);
	return;
}
void add_edge(int u, int v)
{
	edge[current_num].start = v;
	edge[current_num].next = vertex[u].first;
	vertex[u].first = current_num++;
	return;
}
int maxmatch(int u)
{
	int v;
	for (int j = vertex[u].first; j != -1; j = edge[j].next)
	{
		v = edge[j].start;
		if (visited[v] == 0)
		{
			visited[v] = 1;
			if (match[v] == 0 || maxmatch(match[v]))//顶点v未匹配或者存在其他匹配
			{
				match[v] = u;
				match[u] = v;
				return 1;
			}
		}
	}
	return 0;//没有找到匹配时
}
void Print(int total)
{
	cout << "邻接表如下:" << endl;
	for (int i = 1; i <= total; i++)
	{
		cout << "v" << i << "  " << vertex[i].first;
		for (int j = vertex[i].first; j != -1; j = edge[j].next)
			cout << "] -- [" << edge[j].start << "  " << edge[j].next;
		cout << "]" << endl;
	}
}
void Print_flow(int total)
{
	cout << "匹配方案如下:" << endl;
	for (int i = 1; i <= total; i++)
		cout << i << "--" << match[i] << endl;
	return;
}
/*
7 5

1 6
1 8
2 7
2 8
2 11
3 7
3 9
3 10
4 12
4 9
5 10
0 0
*/

运行结果

在这里插入图片描述


参考

数据参考《趣学算法》

发布了166 篇原创文章 · 获赞 45 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_44116998/article/details/104095941