二分图匹配(匈牙利算法)———学习笔记

第一次写博客,有什么错误请指出我会及时改正。qwq

目录

二分图

匹配

最大匹配 

完美匹配

交替路

增广路

代码


  • 二分图

二分图其实就是在一个图中所有的点可以分为两组,同一组中没有边,所有的边都跨越了两个组。准确的说:把一个图的顶点划分为两个不想交的集合 U 和 V ,且使得每一条边都分别lial连接 U 、V 中的顶点,如果存在这样的划分,则称此图为二分图。

此外二分图还有一个等价定义是:不含有「含奇数条边的环」的图。

  • 匹配

二分图匹配就是边的集其中任意两条边没有公共顶点。如图若1—2相连,5—4相连即为此图匹配。1—2相连,5—4相连,7—6相连也为此图匹配。

二分图匹配(匈牙利算法)———学习笔记_第1张图片

我们定义有:

     匹配边、匹配点、非匹配边、非匹配点。

若1—2相连,5—4相连,7—6相连。则显然1—2边、5—4边、7—6边为匹配边,1、2、4、5、6、7为匹配点。剩下的为非匹配点和非匹配边。

  • 最大匹配

一个图的匹配中所含边数最多的匹配即为此图最大匹配。像上图最大匹配即为:

二分图匹配(匈牙利算法)———学习笔记_第2张图片二分图匹配(匈牙利算法)———学习笔记_第3张图片

显然最大匹配可能不只有一种。

  • 完美匹配

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

下面进入正题匈牙利算法。


想学习匈牙利算法先普及几个概念:

  • 交替路:

从一个未匹配点出发,依次经过非匹配边、匹配边、非匹配边……形成的路径叫交替路。如图2—>1—>8—>7即为一条交替路。

二分图匹配(匈牙利算法)———学习笔记_第4张图片

  • 增广路:

从一个未匹配点出发,走交替路,如果途径另一个未匹配点(出发的点不算),则这条交替路称为增广路(agumenting path)。如图1—>2—>3—>6—>7—>8即为一条增广路。

二分图匹配(匈牙利算法)———学习笔记_第5张图片

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

我们可以通过不停地找增广路来增加匹配中的匹配边和匹配点。找不到增广路时,达到最大匹配(这是增广路定理)。匈牙利算法正是这么做的。

下面给出匈牙利算法的dfs代码:

代码:

#include
#include
#include
#include

using namespace std;

int ans,n,m,e,u,v,link[1005],use[1005],map[1005][1005];/*map数组为邻接矩阵,use表示当前点是否匹配,link[i]表示与顶点i所连的点*/ 

inline int read( )
{
	int x = 0;char c = getchar( );
	while (!isdigit(c))
	{
		c = getchar( );
	}
	while(isdigit(c))
	{
		x = (x<<3) + (x<<1) + (c^48);
		c = getchar( );
	}
	return x;
}

inline bool dfs(int x)
{
	for(int i = 1; i <= m; i++)
	{
		if(!use[i]&& map[x][i])//若不在交替路中 
		{
			use[i] = 1;//则加入交替路 
			if(!link[i] || dfs(link[i]))
			{
				link[i] = x;
				return true;
			}
		}
	}
	return false;
}

void xyl( )
{
	memset(link, 0, sizeof(link));
	for(int i = 1; i <= n; i++)
	{
		memset(use, 0, sizeof(use));
		if(dfs(i)) ans++;
	}
}

int main( )
{
	n = read( );
	m = read( );
	e = read( );
	for(int i = 1; i <= e; i++)
	{
		u = read( );
		v = read( );
		map[u][v] = true;
//		map[v][u]=true;
	}
	xyl( );
	printf("%d\n",ans);
	return 0;
}

 

匈牙利算法的要点如下

  1. 从左边第 1 个顶点开始,挑选未匹配点进行搜索,寻找增广路。

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

你可能感兴趣的:(图论)