[笔记]二分图最大匹配--匈牙利算法

[笔记]二分图最大匹配--匈牙利算法

原题链

算法:

0.首先有一些概念:

​ ①.二分图:设G=(V,E)是一个无向图,如果顶点V可分割为两个互不相交的子集(A,B),并且图中的每条边(i,j)所关联的两个顶点i和j分别属于这两个不同的顶点集(i in A,j in B),则称图G为一个二分图。(我并没有看懂上面这一坨,我的理解是可以分成两个相互独立部分的图就是二分图)

​ ②匹配:给定一个二分图G,在G的一个子图M中,M的边集{E}中的任意两条边都不依附于同一个顶点,则称M是一个匹配。

​ ③:最大匹配:最大匹配即是选择其中边数最大的子集的图。

​ ④:增广路:若P是图G中一条连通两个未匹配顶点的路径,并且属于M的边和不属于M的边(即已匹配和待匹配的边)在P上交替出现,则称P为相对于M的一条增广路径(举例来说,有A、B集合,增广路由A中一个点通向B中一个点,再由B中这个点通向A中一个点……交替进行)

​ 我们通过以上概念可以知道,二分图的一组匹配是最大匹配当且仅当图中不存在增广路.

1.算法步骤

​ ①首先所有边都是非匹配边

​ ②寻找增广路,并将增广路两端的点设为已匹配的点

​ ③重复步骤②,知道图中不再有增广路

2.详细过程

​ 具体说一说第②步:

​ 要想新建一条增广路需要满足一下两个条件中的任意一个:

​ 1.右半部分的点y自身为非匹配点

​ 2.右部分的点y为匹配点,但是与它相连的左部分的点x可以在右部分找到另一个点进行匹配

AC代码

#include <bits/stdc++.h>
using namespace std;
bool mapp[5200][5200],vis[5200];
int match[5200];//记录左部分匹配的对象
int n,m,s;
bool dfs(int k){
	for(int i = n + 1;i <= n + m;i++){
		if(!vis[i] && mapp[k][i]){//两点之间有连边,同时没有被访问过
			vis[i] = true;
			if(!match[i] || dfs(match[i])){//满足上面说的两个条件
				match[i] = k;
				match[k] = i;
				return true;
			}
		}
	}
	return false;
}
int main(){
	cin>>n>>m>>s;
	for(int i = 1;i <= s;i++){
		int x,y;
		cin>>x>>y;
		if(x > n || y > m)continue;
		y += n;//处理一下,因为题中给的左右部分的编号相同,不方便标记是否访问过
		mapp[x][y] = mapp[y][x] = true;
	}
	int ans = 0;
	for(int i = 1;i <= n;i++){
		memset(vis,false,sizeof(vis));
		for(int j = 1;j <= n;j++)//默认从左部分向右部分匹配,因此将左部分默认成已访问过
			vis[j] = true;
		if(dfs(i))ans++;
	}
	cout<<ans<<endl;
	return 0;
}

结束

posted @ 2020-08-22 17:04  czyczy  阅读(127)  评论(1编辑  收藏  举报