概述
二分图的最大匹配问题例子:
一共有N 个学生和P 门课程, 一个学生可以任选其中的一门或者多门课程,
问是否可以达成:
1.每个学生代表的都是不同的课;
2.每门课都有不同的代表;
我们看到这就是一个典型的二分图, 学生代号为L点集, 科目代号为R点集;
现在我们要求的就是最多能形成多少对匹配, 然后比较学生数、科目数、匹配对数这三者的关系即可。
现在的问题就是求二分最大匹配的问题
在这里这种问题有三种常见的解决方法: 匈牙利算法、 最大流算法、 Dinic算法。 在这里我们先讲前两种。
现在我们遇到了一个有向有圈图的问题 无法求出讲解过程的拓扑序, 这也就导致了在下面的讲解中会用到后几篇博客的知识
匈牙利算法
匈牙利算法可以认为是深度优先搜索(DFS)的一个应用。(匈牙利算法有dfs和bfs两种)
在这里我们要明确, 匈牙利算法是用来求二分图的最大匹配的, 他的核心问题就是求增广路。
匈牙利算法的时间复杂的为O(V * E) V是开始点集的点数, E是中间的边数
我们再来复习一下增广路的特性:
(1)有奇数条边。
(2)起点在二分图的左半边,终点在右半边。
(3)路径上的点一定是一个在左半边,一个在右半边,交替出现。
(4)整条路径上没有重复的点。
(5)起点和终点都是目前还没有配对的点,而其它所有点都是已经配好对的。
(6)路径上的所有第奇数条边都不在原匹配中,所有第偶数条边都出现在原匹配中。
(7)最后,也是最重要的一条,把增广路径上的所有第奇数条边加入到原匹配中去,并把增广路径中的所有第偶数条边从原匹配中删除(这个操作称为增广路径的取反),则新的匹配数就比原匹配数增加了1个。
二分图的最大匹配问题是很多图论的基础问题 具体看上一篇博客:
传送门 加粗的文字标出
上代码:
//不定长数组模拟邻接链表实现
//linker[i] = j 表示 i 和 j 连接
//used表示在当前寻找增广路的时候是否寻找过该点
bool dfs(int u)
{
for (int i = 0; i < G[u].size(); i++)
{
int v = G[u][i];
if (!used[v])//在此次递归的时候V并没有使用过
{
used[v] = true;
if (linker[v] == -1 || dfs(linker(v)))
{//将v与u链接的条件是 v节点没有使用 || 现在连接V节点的那个节点除了V之外还有其他选择
linker[v] == u;
return true;
}
}
}
return false;//遍历全部的Y点集的点 仍找不到增广路
}
int hungary(void)
{
int res;
memset(linker, -1, sizeof(linker));
for (int u = 0; u < V; u++)
{
memset(used, 0, sizeof(used));
if (dfs(u))
res++;
}
return res;
}
最大流算法
我们在点集X的左侧加上一份起始点S, 在右侧点集Y的右侧加上一个终点T, 现在二分图的最大匹配就成了如下图的最大流图(每条边的权值为1; 注意是全为1)
现在我们使用ford-fulkerson算法的特殊情况(边的权值全为1)
详细的分析见图论的下几篇博客最大流的博客 现在直接上代码
#include<queue>
#include<vector>
#include<cstring>
#include<cstdio>
using namespace std;
vector<int>G[1000];
int match[1000];
bool used[1000];
int V;
void add_edge(int u, int v)
{
G[u].push_back(v);
G[v].push_back(u);
}
bool dfs(int v)
{
used[v] = true;
for (int i = 0; i < G[v].size(); i++)
{
int u = G[v][i];
int w = match[u];
if (w < 0 || !used[w] && dfs(w))// 这一步和匈牙利算法很像 但是表示的意义不同
{//详见最大流算法
match[v] = u;
match[u] = v;
return true;
}
}
return false;
}
int bipartite_mathing()
{
int res = 0;
memset(match, 0, sizeof(match));
for (int v = 0; v < V; v++)
{
if (match[v] < 0)
{
memset(used, 0, sizeof(used));
if (dfs(v))
res++;
}
}
return res;
}