假期 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的一条增广路径。表型形式是一条交错路径,这条由边组成的路径,它的第一条边没有参与匹配,第二条边参与匹配,第三条边没有参与匹配,最后一条边也没有参与匹配,并且始点和终点没有匹配。另外,单独的一条连接两个未匹配点的也显然是交错路径。
- 在步骤一的情况,下增加匹配的个数。
- 当找不到增广路径时,即找到了最大匹配,结束程序。
用途:主要用于解决一些与二分图匹配有关的问题,即部图匹配最常见的算法。
题目描述
某公司存在分工问题,一定数量的女员工与男员工搭配,如何搭配才能实现人员的最大利益,即最大匹配问题。
思路分析
见匈牙利算法介绍
算法分析
- 构建网络,创建邻接表
- 初始化所有的顶点未访问
- 依次检查u的邻接点v,如果v未被访问,则将v设置为被访问,并且判断v是否已经匹配,若没有,则u,v匹配,否则从v的邻接点出发,检查是否有增广路径,如果有,就沿增广路径反色,再u v匹配,否则返回0
- 当找不到增广路径时,即找到了最大匹配,结束程序
代码解析
#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
*/
运行结果
参考
数据参考《趣学算法》