简介
二分图是一种特殊的图。其定义为:
节点由两个集合组成,且两个集合内部没有边的图。
那啥时候可以用二分图呢?当且仅当图可以进行合法的黑白染色的时候,可以考虑二分图。
二分图匹配
匈牙利算法
最基础的问题就是二分图的最大匹配。所谓最大匹配,就是对于左右两个集合内的点,每个点都只能选一次,并且左右匹配的点之间有连边,求最多能左右匹配的点对数。举个最简单的例子,就是有 n n n 个男生和 m m m 个女生,之间有相互喜欢的关系,求最终最多能牵几根红线。
由于这个问题比较基础,所以具体可以看网上其它博客,这里仅作简述。
我们常用的方法是匈牙利算法,这个算法,你可以感性地将之理解为一个有礼貌地戴绿帽的过程。
算法流程
- 从 1 ∼ n 1\sim n 1∼n 便利每个男生。
- 对于每个男生,先随便找一个两情相悦的女生。然后分两种情况:
1 1 1、 这个女生没有匹配,那么就匹配上。
2 2 2、 这个女生已经匹配了,那么就礼貌地问问那个匹配她的男生:“你好,我可以把你戴绿帽吗?”然后问题转化为那个可悲的被戴绿帽的男生去匹配其他女生的问题,可以递归解决。 - 然后如果可以通过一连串的戴绿帽,就可以使答案加一。
这就是匈牙利的全过程,更具体的推荐扶咕咕的题解。
Code
#include<bits/stdc++.h>
#define ll long long
#define pb push_back
#define inf 1<<30
using namespace std;
const int MAXN=510;
int n,m,e,mtc[MAXN];
bool used[MAXN],edge[MAXN][MAXN];
bool find(int x){
for(int i=1;i<=m;i++){
if(used[i]||!edge[x][i]) continue;
used[i]=1;//标记在当前处理时,该女生是否访问。
if(mtc[i]==0||find(mtc[i])){
mtc[i]=x;
return 1;
}
}return 0;
}
int main()
{
scanf("%d%d%d",&n,&m,&e);
for(int i=1,u,v;i<=e;i++){
scanf("%d%d",&u,&v);
edge[u][v]=1;
}int ans=0;
memset(mtc,0,sizeof(mtc));
for(int i=1;i<=n;i++){
memset(used,0,sizeof(used));
if(find(i)) ans++;
}printf("%d\n",ans);
}
Ps:一个优化
显然,匈牙利的复杂度是 O ( n 2 ) O(n^2) O(n2) 的,所以有人看到百万级别的题就不会往二分图匹配上去想了。这显然不对,因为有的题目每个点连出去的边只有几条,但是点却是上万的,此时,如果在遍历的时候用 v e c t o r vector vector 或者前向星存边,那么就可以只跑必定有边的匹配,可以大大降低复杂度。例如这道题,就可以这么做:
#include<bits/stdc++.h>
#define ll long long
#define pb push_back
#define inf 1<<30
#define INF 1ll<<60
using namespace std;
const int MAXN=1e6+10;
vector<int> e[MAXN];
int vis[MAXN],mtc[MAXN];
bool find(int x,int t){
for(