二分图最大匹配学习
- 一.二分图的基本知识
- 二.二分图最大匹配
- 什么是二分图最大匹配
- 怎么求二分图最大匹配
- 三.二分图最大权匹配
- 四.例题训练
- 三.最小点覆盖数
一位大佬的神级解释
本以为有了网络流,就不用再学匈牙利了,但在做题的过程中,发现有些题不能用网络流做,而只能用匈牙利做。迫不得已,只能来学匈牙利了。
一.二分图的基本知识
1.定义
- G=(V, E)是一个无向图,若能将V分割成两个互不相关的点集X,Y,且图中每条边连接的两个顶点一个在 X中,另一个在 Y中,则称图G为二分图。
2.性质与判定
- 定理:当且仅当无向图G的每一个环的结点数均是偶数时,图G才是一个二分图。如果无环,相当于每的结点数为 0,故也视为二分图。
判定方法:dfs23染色法
- 过程:我们把X部的结点颜色设为2,Y部的颜色设为3。
- 从某个未染色的结点u开始跑dfs。把u染为0,枚举u的儿子v。如果v未染色,就染为与u相反的颜色,如果已染色,则判断u与v的颜色是否相同,相同则不是二分图。
- 如果一个图不连通,则在每个连通块中作判定。
代码如下:(链式前向星存图)
#include<bits/stdc++.h>
using namespace std;
const int N=1e3+10;
const int M=N*N*2;
struct ppp{int u,v,next;
}e[M];
int vex[N],vis[N],k,flag=1;void add(int u,int v){k++;e[k].u=u;e[k].v=v;e[k].next=vex[u];vex[u]=k;
}
void dfs(int u){for(int i=vex[u];i;i=e[i].next){int v=e[i].v;if(vis[v]==0){vis[v]=vis[u]^1;dfs(v);}else if(vis[v]==vis[u]){flag=0;}}
}
int main(){int n,m,u,v;cin>>n>>m;for(int i=1;i<=m;i++){cin>>u>>v;add(u,v);}for(int i=1;i<=n;i++){if(!vis[i]){vis[i]=2; dfs(i);}} return 0;
}
二.二分图最大匹配
什么是二分图最大匹配
- 定义二分图两部分的点分别称为左部点和右部点。
- 一张图的匹配是一些没有公共端点的边,最大匹配即没有公共端点的边的边数。
- 可以想象,对于一个二分图而言,显然一个匹配是对于每一个左部点,连一条边向一个右部点,或者不向右部点连边。但是每个左部点不会对应两个或以上的右部点。
怎么求二分图最大匹配
方法1:匈牙利算法
- 枚举每一个左部点u ,然后枚举该左部点连出的边,对于一个出点v,如果它没有被先前的左部点匹配,那么直接将u 匹配v,否则尝试让v的“原配”左部点去匹配其他右部点,如果“原配”匹配到了其他点,那么将u 匹配v,否则u 失配。
- 尝试让“原配”寻找其他匹配的过程可以递归进行。需要注意的是,在一轮递归中,每个右部点只能被访问一次。
- 个人理解匈牙利算法:判断是否能匹配第 i 个人,是在之前的所有匹配成功的左部点不会匹配不到人的前提下,尝试能不能让第 i 个点匹配到人。
- 时间复杂度:O(nm)
代码实现如下
#include <bits/stdc++.h>
using namespace std;
const int N=1005;
struct ppp{int u,v,next;
}e[N*N];
int n1,n2,m;
int cp[N],vis[N],vex[N],k;void add(int u,int v) {k++;e[k].u=u;e[k].v=v;e[k].next=vex[u];vex[u]=k;
}int dfs(int u,int tag) {//tag为时间戳,保证每次dfs点不重复经过 if(vis[u]==tag)return 0; vis[u]=tag;for(int i=vex[u];i;i=e[i].next){int v=e[i].v;if(!cp[v]||dfs(cp[v],tag)){//如果喜欢的人单身,直接在一起//如果喜欢的人有对象,让他对象再找一个对象;//1.如果找得到,喜欢的人的对象换掉,喜欢的人成为自己的对象//2.如果找不到,这个喜欢的人就没机会了(找下一个喜欢的人) cp[v]=u;return 1;}}return 0;
}int main() {int u,v;cin>>n1>>n2>>m;for (int i=1;i<=m;i++) {cin>>u>>v;add(u,v);}int ans=0;for (int i=1;i<=n1;i++) {if (dfs(i, i))++ans;//让左部点 i去找对象 }cout<<ans;return 0;
}
方法2:转化为最大流模型
- 将源点与左端点连条容量为1的边,右端点向汇点连一条容量为1的边,左部点与右部点连一条容量为1的边。那么二分图的最大匹配就是网络流的最大流。
一些定理:
- 最小点覆盖:即最少的点去覆盖所有的边。
方法:最小点覆盖等于最大匹配数 - 最大独立集:选最多的点,满足两两之间没有边相连。
方法:最大独立集等于 n - 最大匹配数
三.二分图最大权匹配
什么是二分图最大权匹配
- 边附上了边权。在最大匹配下,求边权和最大的匹配。
方法:转化为最小费用最大流模型(自行转最大费用最大流)
- 源点向左部点连一条容量为1,费用为0的边。左部点向右部点连一条容量为1,费用为边权的边。右部点向汇点连一条容量为,费用为0的边。跑最小费用最大流模板
四.例题训练
主要还是考建模能力
例题1
例题2
- 例题1:让人跟床做完全匹配即可
- 例题2:让外籍飞行员和英国飞行员求最大匹配即可(更模板)
例题3
这就是我说的那道网络流不行的题
- 问题分析:使用匈牙利算法。建模:让属性值a,b,分别连向同一个右部点,保证这两个值不会同时匹配在同一个右部点上。让攻击序列作为左部点,去和武器属性做匹配。匈牙利的过程中若第 i 个点没有匹配,则婷婷。(这个点就是网络流做不到的地方,匈牙利可以挨个去判断第 i 个点是否能匹配到点)。
三.最小点覆盖数
- 对于一个二分图,求其的最小点覆盖数,即用最少的点覆盖所有的边。
定理:最小点覆盖数等于最大匹配数
扩展:最小边覆盖=最大独立集=总节点数-最大匹配数