匈牙利算法—二分图的最大匹配
1:概念:
匹配:在图论中,一个「匹配」是一个边的集合,其中任意两条边都没有公共顶点;
最大匹配:一个图所有匹配中,所含匹配边数最多的匹配,称为这个图的最大匹配;
完美匹配:如果一个图的某个匹配中,所有的顶点都是匹配点,那么它就是一个完美匹配;
2:原理:
先将图化为左右两边(必须为二分图)
左侧:n, 右侧:m
定义数组:used[]: 标记数组
s[]: 每个右点与之匹配的左点编号,开始时清零
遍历左边各点:1:每次将used标记数组清零;
2: find()成立,找到,即可将sum++;
//整体
for(int i=1;i<=n;i++){
memset(used,0,sizeof(used));
if(find(i)) sum++;
}
下面是寻找与之匹配的各点:(核心算法:dfs)
方式:对能够到达的右点查找:(并且是没查找过的)
如果该右点没有匹配过,或者 匹配过但是递归回去可以将原本匹配好的左点改为另一个右点对应,则可以将本左点与该右点匹配
否则继续寻找,或false
//邻接矩阵版本:
bool find(int i){
for(int j=1;j<=m;j++){
if(used[j]==0 && g[i][j]){
used[j] = 1;
if(s[j]==0 || find(s[j])){
s[j] = i;
return true;
}
}
}
return false;
}
//邻接表版本:
bool find(int i){
for(int j=0; j<g[i].size();j++){
int x = g[i][j];
if(used[x]==0){
used[x] = 1;
if(s[x]==0 || find(s[x])){
s[j] = i;
return true;
}
}
}
return false;
}
3:题目
模板题:洛谷3386
//邻接表做法
#include <bits/stdc++.h>
#define maxn 550
using namespace std;
int n,m,e;
int used[maxn],s[maxn];
vector<int> g[maxn];
bool check(int a,int b){
for(int i=0;i<g[a].size();i++){
if(g[a][i] == b) return false;
}
return true;
}
bool find(int i){
for(int j=0;j<g[i].size();j++){
int x = g[i][j];
if(used[x]==0){
used[x] = 1;
if(s[x]==0 || find(s[x])){
s[x] = i;
return true;
}
}
}
return false;
}
int main(){
cin>>n>>m>>e;
while(e--){
int a,b;
cin>>a>>b;
if(check(a,b))
g[a].push_back(b);
}
int sum = 0;
for(int i=1;i<=n;i++){
memset(used,0,sizeof(used));
if(find(i)) sum++;
}
cout<<sum<<endl;
}