我是靠谱客的博主 直率棒棒糖,最近开发中收集的这篇文章主要介绍二分图之匈牙利算法,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

主要内容:二分图的最大匹配,完美匹配,最小路径覆盖数,匈牙利算法
二分图相关概念

1.二分图:把一个图的顶点划分为两个不相交集U和V,使每一条边都分别连接U、V中的顶点。
无向图G为二分图的充分必要条件是,G至少有两个顶点,且其所有回路的长度均为偶数。即是说,一个图要么没有环,要么有偶数边的环。所以二分图的另一个定义为:不含有「含奇数条边的环」的图。
这里写图片描述

2.匹配:在图论中,一个「匹配」(matching)是一个边的集合,其中任意两条边都没有公共顶点。例如,图 3、图 4 中红色的边就是图 2 的匹配。
这里写图片描述
其中红线为匹配边,匹配边链接的连个点就是匹配点

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

4.完美匹配:如果一个图的某个匹配中,所有的顶点都是匹配点,那么它就是一个完美匹配。图 4 是一个完美匹配。完美匹配一定是最大匹配,但并非每个图都存在完美匹配。

5.最小点覆盖数:选取最少的点,使任意一条边至少有一个端点被选择。最小覆盖要求用最少的点(XX集合或YY集合的都行)让每条边都至少和其中一个点关联。可以证明:最少的点(即覆盖数)=最大匹配数

6.最大独立数:选取最多的点,使任意所选两点均不相连。

7.最小路径覆盖数:对于一个 DAG(有向无环图),选取最少条路径,使得每个顶点属于且仅属于一条路径,路径长可以为 0(即单个点)。也就是说用尽量少的不相交简单路径覆盖DAG的所有结点。解决此类问题可以建立一个二分图模型。把所有顶点i拆成两个:X结点集中的iY结点集中的i′,如果有边i→j,则在二分图中引入边i→j′,设二分图最大匹配为m,则结果就是n-m。

从上述概念描述中可以得到三个定理:

定理1:最大匹配数 = 最小点覆盖数(这是 Konig 定理)

定理2:最大匹配数 = 最大独立数

定理3:最小路径覆盖数 = 顶点数 - 最大匹配数

匈牙利算法
1.概述:求解最大匹配问题的一个算法是匈牙利算法
用白话说:就是从二分图中找出一条路径来,让路径的起点和终点都是还没有匹配过的点,并且路径经过的连线是一条没被匹配、一条已经匹配过,再下一条又没匹配这样交替地出现。找到这样的路径后,显然路径里没被匹配的连线比已经匹配了的连线多一条,于是修改匹配图,把路径里所有匹配过的连线去掉匹配关系,把没有匹配的连线变成匹配的,这样匹配数就比原来多1个。不断执行上述操作,直到找不到这样的路径为止。

这里写图片描述

2.交替路:从一个未匹配点出发,依次经过非匹配边、匹配边、非匹配边…形成的路径叫交替路。

3.增广路:从一个未匹配点出发,走交替路,如果途径另一个未匹配点(出发的点不算),则这条交替路称为增广路(agumenting path)。例如,图 5 中的一条增广路如图 6 所示(图中的匹配点均用红色标出)

这里写图片描述

广路有一个重要特点:非匹配边比匹配边多一条。因此,研究增广路的意义是改进匹配。只要把增广路中的匹配边和非匹配边的身份交换即可。由于中间的匹配节点不存在其他相连的匹配边,所以这样做不会破坏匹配的性质。交换后,图中的匹配边数目比原来多了 1 条。

4.匈牙利算法核心:不停地找增广路来增加匹配中的匹配边和匹配点。找不到增广路时,达到最大匹配(这是增广路定理)。

5.匈牙利树一般由 BFS 构造(类似于 BFS 树)。从一个未匹配点出发运行 BFS(唯一的限制是,必须走交替路),直到不能再扩展为止。

例如,由图 7,可以得到如图 8 的一棵 BFS 树:

这里写图片描述
这棵树存在一个叶子节点为非匹配点(7 号),但是匈牙利树要求所有叶子节点均为匹配点,因此这不是一棵匈牙利树。如果原图中根本不含 7 号节点,那么从 2 号节点出发就会得到一棵匈牙利树。这种情况如图 9 所示(顺便说一句,图 8 中根节点 2 到非匹配叶子节点 7 显然是一条增广路,沿这条增广路扩充后将得到一个完美匹配)。

BFS&DFS代码实现
匈牙利算法的要点如下:

1.从左边第 1 个顶点开始,挑选未匹配点进行搜索,寻找增广路。
__ 1.如果经过一个未匹配点,说明寻找成功。更新路径信息,匹配边数 +1,停止搜索。
__ 2.如果一直没有找到增广路,则不再从这个点开始搜索。事实上,此时搜索后会形成一棵匈_____牙利树。我们可以永久性地把它从图中删去,而不影响结果。

2.由于找到增广路之后需要沿着路径更新匹配,所以我们需要一个结构来记录路径上的点。DFS 版本通过函数调用隐式地使用一个栈,而 BFS 版本使用 prev 数组。

DFS_CODE

const int MAX=1010;
int k,n,m,ma[MAX][MAX],vis[MAX],match[MAX];
//k,n,m分别为边的个数,集合U的个数,集合V的个数。ma[][]记录边联通的边,vis记录访问过的点,match记录已经匹配的点
bool DFS(const int &p){
for(int i=1;i<=m;i++){
if(!vis[i] && ma[p][i]){
vis[i]=1;
if(!match[i]||DFS(match[i])) //判断是否是增广路
{
match[i]=p;
//更新路径//也是取反的过程
return true;
}
}
}
return false;
}
int main()
{
//....do something //初始化//用邻接矩阵记录边
int ans=0;
for(int i=1;i<=n;i++){
memset(vis,0,sizeof(vis));
//刷新访问
if(DFS(i))
//如果找到增广路,所匹配的边数加一
ans++;
}
//....do something
return 0;
}

REF:

  • T.L BLOG
  • Renfei Song’s Blog
  • Matrix67: The Aha Moments

最后

以上就是直率棒棒糖为你收集整理的二分图之匈牙利算法的全部内容,希望文章能够帮你解决二分图之匈牙利算法所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(32)

评论列表共有 0 条评论

立即
投稿
返回
顶部