设$G=(V,E)$($V$为点集,$E$为边集)是一个无向图,如果顶点$V$可分割为两个互不相交的子集$(A,B)$,并且图中的每条边$(i,j)$所关联的两个顶点$i$和$j$分别属于这两个不同的顶点集  $(i \in A,j \in B)$,则称图$G$为一个二分图……
一 定义
1 什么是二分图
- 设$G=(V,E)$($V$为点集,$E$为边集)是一个无向图,如果顶点$V$可分割为两个互不相交的子集$(A,B)$,并且图中的每条边$(i,j)$所关联的两个顶点$i$和$j$分别属于这两个不同的顶点集  $(i \in A,j \in B)$,则称图$G$为一个二分图。
 
如下图,一张图被分为$U$,$V$两个点集,每个点集内部均无边,则是一个二分图。

2 什么是匹配
在一个二分图$G$中选出$k$条边,组成新图$G’$,使得任意一条边的两个端点都与其他边的端点不重合,则称$G’$为$G$的一个匹配。
上图中,若$U$集编号从上至下为$1,2,3,4,5$,$V$集编号从上至下为$a,b,c,d$,则$(2,b),(5,a)$是一个匹配。
3 最大匹配
在一个二分图$G$中,匹配$G_m$的边数最大(仅考虑不带权匹配),则称$G_m$为$G$的最大匹配。
上图中,若$U$集编号从上至下为$1,2,3,4,5$,$V$集编号从上至下为$a,b,c,d$,则$(1,a),(2,b),(3,c),(5,d)$是最大匹配。
4 增广与增广路
从$U$集一未匹配点开始,找一个并$V$集的未匹配点并进行匹配(前提是有连边)的过程称为增广。
连接两个新匹配点间的边被称为增广路。
二 判定
二分图的判定可以有染色法解决。
因为二分图中不存在奇圈,故若用两种颜色将二分图中的每个节点染色,且使得相邻的节点颜色不同,若存在一种合法方案,就可以判定图是二分图。
用dfs模拟染色过程,一旦发现颜色冲突就跳出染色,否则返回染色成功即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
   | const int CP=1e4; const int CE=1e5;
  class fs{   public:       int to,nxt; }E[CE]; int hd[CP],ecnt=0; void add(int x,int y){     E[++ecnt].to=y;     E[ecnt].nxt=hd[x];     hd[x]=ecnt; }
 
  int col[CP];                              bool dfs(int u,int c){     col[u] = c;     for(int k=hd[u]; k; k=E[k].nxt){         int to=E[k].to;         if(col[to] == c) return false;          if(!col[to] && !dfs(to,-c)) return false;      }     return true; } bool examine(){     for(int i=1;i<=n;i++)         if(!col[i]){             int flag = dfs(i,1);             if(!flag) return false;         }     return true; }
   | 
 
三 匈牙利算法
1 流程

step1
以$1$为起点,找一条增广路,选中$(1,a)$(蓝线)。
step2
以$2$为起点,找一条增广路,试图选中$(2,a)$(红线)。发现$a$已被匹配,故从$2$开始,走匹配边(蓝线)到$1$,试图寻找增广路,失败。故$a$无法失配,配对失败。
此处若能寻找到增广路$(1,x)$,则$a$失配并与$2$配对,$1$重新与$x$配对,因为这样实际上增加了一条匹配边。
step 3
继续以$2$为起点,找一条增广路,选中$(2,c)$(蓝线)。
step 4
以$3$为起点,找一条增广路,选中$(3,b)$(蓝线)。
至此,左图中所有点皆以匹配完成,算法结束。
2 代码实现(match)
判断能否增广的过程需要一遍dfs,且需要知道 能(true)与否(false)。故我们需要定义一个返回值为bool的dfs。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
   | 
 
 
 
 
  const int CON=2e3+3;
 
  class fs{   public:     int to,nxt; }E[CON]; int hd[CON],ecnt=0; void add(int x,int y) {     E[++ecnt].to=y;     E[ecnt].nxt=hd[x];     hd[x]=ecnt; }
  bool ins[CON];  int mtclf[CON];  int mtcrt[CON]; 
  bool dfs(int cur) {     for(int k=hd[cur]; k; k=E[k].nxt)     {          int to=E[k].to;                   if(!ins[to])          {             ins[to] = true;                          if(!mtcrt[to] || dfs(mtcrt[to]))              {                 mtclf[cur] = to;                  mtcrt[to] = cur;                                  return true;              }         }     }          return false;  }
  int match() {     int ret=0;           for(int st=1; st<=n; st++)          if(!mtclf[st])         {             memset(ins,false,sizeof(ins));                          if(dfs(st))                 ret++;          }                  return ret;  }
 
  |