#二分图
概念
二分图是这样的一个图,它的顶点可以分为两个集合X和Y。所有的边关联的两个顶点中,恰好一个属于集合X,一个属于集合Y。
二分图匹配
给定一个二分图G,M为G边集的一个子集,如果M满足当中的任意两条边都不依附于同一个顶点,则称M是一个匹配。
最大匹配
图中包含边数最多的匹配称为图的最大匹配。
完美匹配
完美匹配: 如果所有点都在匹配边上,称这个最大匹配是完美匹配。
二分图判定
染色法,对一个节点染色,对其相邻不同节点染不同颜色。如果发现某个节点染色后,与其相邻节点染上了同一个颜色,则此图不是二分图
二分图最大匹配
- 匈牙利算法 O ( m n ) O(mn) O(mn)
- 转化为单位容量简单网络的最大流问题
最大流不会,不管,讲匈牙利算法。其思想是用宽度优先搜索来找增广路径
增广路(也称增广轨或交错路)
若P是图G中一条连通两个未匹配顶点的路径,并且属M的边和不属M的边(即匹配边和非匹配边)在P上交替出现,则称P为相对于M的一条增广路径。(通俗地讲,就是匹配边,非匹配边ji)
性质:
- 长度是奇数
- 第1,3,5…条边是非匹配边,第2,4,6…条边是匹配边。即匹配边永远比非匹配边多1条
正是因为有了如上性质,我们把路径上的所有状态取反,那么得到的新的边集合是 S ′ S' S′任然是一组匹配,并且匹配数增加了1。进一步可以推出以下结论:
- 二分图的一组匹配S是最大匹配,当且仅当图中不存在S的增广路。
交替路
[外链图片转存失败(img-54GA3ce8-1565683140692)(https://leanote.com/api/file/getImage?fileId=5c499d9bab64415185001a5f)]
如图,路径x4→y3→x2→y1→x1→y2是一条交替路。
匈牙利算法
步骤
- 设 S = ∅ S = \emptyset S=∅,即所有的边均为非匹配边
- 寻找增广路 p a t h path path,把路径上的所有边的匹配状态取反,得到一个更大的集合 S ′ S' S′.
- 重复2,直到不存在增广路
该算法关键是如何找到增广路。匈牙利算法依次尝试给每一个左边节点 x x x寻一个匹配的右边节点 y y y。右边节点y能与左边匹配,需要满足以下两个条件之一:
- y本身就是非匹配点。此时(x,y)本身就是非匹配边,自己构成长度为1的增广路
- y已经与左部x’匹配,但从x’出发能找到另一个右部点y’与之匹配
此时,xyx’~y’为一条增广路。如图:
[外链图片转存失败(img-Kolu66ma-1565683140694)(https://leanote.com/api/file/getImage?fileId=5c49a44cab64415185001a9e)]
匹配过程
假如我们用xM数组表示左边节点对其右边节点的匹
配,
yM表示右边节点对其左边节点的匹配,初始化为-1;现在重点看节点3,当寻找下一条边时,如图中的蓝边,我们发现节点6的yM[6]=2;已经匹配了.此时我们就转到节点6的匹配点2上去,发现节点2的另一条边2->5中节点5也已经匹配了,yM[5]=1;继续转到节点1,发现节点1的边1->4中节点4还没匹配.于是我们找到了一个增广路径
所以总的流程可以如下:
- 对于一个未匹配的节点u,寻找它的每条边,如果它的边上的另一个节点v还没匹配则表明找到了一个匹配,直接转步骤4;
- 假如节点u它边上的另一个节点v已经匹配,那么就转向跟v匹配的节点,假设是w,然后再对w重复1,2的步骤,即寻找增广路
- 假如我们在1,2步过程中找到一条增广路,那么修改各自对应的匹配点,转4,若无增广路, 则退出.
- 匹配数+1
int find(int x){
for(int i = head[x];i;i = edges[i].next){
int t = edges[i].t;
if(!vis[t]){
vis[t] = true;
if(match[t]==0||find(match[t]){
//找到就匹配,没找到就看一下上面的节点能不能找到
match[t] = x;return true;
}
}
}
return false;
}
for(int i = 1;i<=n;i++){
memset(vis,0,sizeof(vis));
if(find(i))ans++;
}
eg:woj1669(注意dfs里面写的是dfs(match[t])而不是dfs(t))
#include <iostream>
#include <cstring>
using namespace std;
const int MAXN = 410;
const int MAXM = MAXN*MAXN;
struct edge{
int next,t,w;}edges[MAXM<<1];
int head[MAXN],top;
void add(int f,int t,int w){
edges[++top].next = head[f];
edges[top].t =t ;
edges[top].w = w;
head[f] = top;
}
bool vis[MAXN];
int match[MAXN];
bool dfs(int x){
for(int i=head[x];i;i=edges[i].next){
int t=edges[i].t;
if(!vis[t]){
vis[t]=true;
if(match[t]==0||dfs(match[t])){
match[t]=x;
return true;
}
}
}
return false;
}
int main(){
int n,m;
cin>>n>>m;
for