[笔记]二分图最大匹配--匈牙利算法
原题链
算法:
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;
}