二分图最大匹配之匈牙利算法
如此之菜的笔者现在才学会
板子题 - Luogu P3386 【模板】二分图最大匹配
挂个链接 - https://www.luogu.com.cn/prob...
题面 - 给定一个二分图,其左部点的个数为n,右部点的个数为m,边数为 e,求其最大匹配的边数
Now,让我们开始学习匈牙利算法
匈牙利是用增广路求最大匹配的算法
增广路的定义,若P是图G中一条连通两个未匹配顶点的路径,并且属于M的边和不属于M的边(即已匹配和待匹配的边)在P上交替出现,则称P为相对于M的一条增广路径,M为G的最大匹配当且仅当不存在M的增广路径
红红火火恍恍惚惚
你是不是很懵呀,是不是感觉如此高大上,其实非常简单,笔者是故意说的这么复杂的,换一种说法你就明白了
笔者就用牛和牛栏来解释吧(其实本来想用撮合情侣来解释的)
现在有n头牛,m个牛栏,牛只能在它喜欢的牛栏中且一个牛栏一头牛,告诉你每头牛喜欢哪些牛栏,问你最多能安排多少对牛和牛栏
要你干什么你明白了吧,现在就是怎么做的问题了
现在开讲匈牙利算法(这次是正经的)
举个栗子吧,这样会清晰一些
这是题目给你的条件
下面就要利用这些条件进行匈牙利算法了
先看到牛1,去找它喜欢的牛栏有没有空着的
发现它喜欢的牛栏1空着,那就占下牛栏1
牛1有牛栏了,下面看牛2,再去找牛2喜欢的牛栏有没有空着的
发现它喜欢的牛栏2空着,那就占下牛栏2
接着看牛3,去找牛3喜欢的牛栏有没有空着的
它喜欢的牛栏没有空位
这时候他就要让其它的牛设法给腾出自己想要的牛栏,这是个递归的过程
首先找到牛3喜欢的牛栏1现在的主人牛1手上,于是便让牛1给它腾位置
牛1只好去找别的位置,发现自己喜欢的牛栏没有也没有空位
于是它找到牛栏2现在的主人牛2,让牛2给自己腾位置
牛2发现自己还有牛栏3空着,便占下牛栏3,开始递归回溯
牛2把牛栏2腾给了牛1,牛1占下牛栏2
牛1把牛栏1腾给了牛3,牛3占下牛栏1
最后是牛4,去找牛4喜欢的牛栏又没有空着的
发现没有,便递归请其它牛帮它腾牛栏,结果腾不出来(递归过程可以自己尝试梳理一下)
于是最终答案就是这样
这样的递归如何用程序实现呢
请看下方(我叫它DFS,似乎更多是叫Find)
bool DFS(int x){
vis[x]=1; //标记已访问过该点
for(int i=0; i<v[x].size(); i++)
if(have[v[x][i]]==0){ //如果有喜欢的牛栏是空的
have[v[x][i]]=x; //占下该牛栏
return true;
}
//否则没有喜欢的牛栏是空的
for(int i=0; i<v[x].size(); i++){ //依次访问喜欢的牛栏的现主人,请求其腾出牛栏
int temp=have[v[x][i]];
if(vis[temp]!=0){
continue; //与上面的vis[]相呼应,用于避免重复访问从而进入死循环
}
if(DFS(temp)){ //如果能腾出牛栏
have[v[x][i]]=x; //占下该牛栏
return true;
}
}
return false;
}
一定要记得判段重复啊(笔者就在这上面被坑了,直接死循环爆栈)
OK,这就基本结束啦~o(* ̄▽ ̄*)o
最后照例奉上完整程序和精心设计的注解
//Luogu P3386 [普及+/提高] - 二分图最大匹配
//https://www.luogu.com.cn/problem/P3386
//匈牙利算法
#include<bits/stdc++.h>
using namespace std;
int have[505]={0}; //记录牛栏的主人
int vis[505]; //标记已访问
vector<int> v[505]; //存放牛喜欢的牛栏
bool DFS(int x){
vis[x]=1; //标记已访问过该点
for(int i=0; i<v[x].size(); i++)
if(have[v[x][i]]==0){ //如果有喜欢的牛栏是空的
have[v[x][i]]=x; //占下该牛栏
return true;
}
//否则没有喜欢的牛栏是空的
for(int i=0; i<v[x].size(); i++){ //依次访问喜欢的牛栏的现主人,请求其腾出牛栏
int temp=have[v[x][i]];
if(vis[temp]!=0){
continue; //与上面的vis[]相呼应,用于避免重复访问从而进入死循环
}
if(DFS(temp)){ //如果能腾出牛栏
have[v[x][i]]=x; //占下该牛栏
return true;
}
}
return false;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
int N, M, E;
int ans=0;
cin >> N >> M >> E;
for(int i=1; i<=E; i++){
int a, b;
cin >> a >> b;
v[a].push_back(b);
}
for(int i=1; i<=N; i++){
for(int j=1; j<=N; j++)
vis[j]=0; //vis[]记得每次都要清零
if(DFS(i))
ans++;
}
cout << ans;
return 0;
}
最后的最后,有问题欢迎在下方评论区提出呀~
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。