二分图

二分图

这里有一个讲义链接

定义

如果一张无向图的N个节点(N≥2)可以分成A,B两个非空集合,其中A∩B=Ø,并且在同一集合内的点之间都没有边相连,那么称这张无向图为一张二分图。A,B分别称为二分图的左部和右部。

二分图判定

一张无向图是二分图,当且仅当图中不存在奇环(长度为奇数的环)。

实现判定的方法(染色法)

二分图最大匹配

下边是一些奇奇怪怪的定义:

任意两条边都没有公共端点的边被称为图的一组匹配。二分图中包含边数最多的一组匹配被称为二分图的最大匹配

对于任意一组匹配S,属于S的边被称为匹配边,与之对应的,不属于S的边被称为非匹配边。匹配边的端点被称为匹配点,相反,其他节点被称为非匹配点。如果在二分图中存在一条连接两个非匹配点的路径path,使得非匹配边与匹配边在path上交替出现,那么称path是匹配S的增广路

增广路具有下列性质:

  1. 长度len是奇数(因为以非匹配边起以非匹配边终)
  2. 路径上第奇数条边是非匹配边,第偶数条边是匹配边。

所以二分图的一组匹配S是最大匹配,当且仅当图中不存在S的增广路。

 

匈牙利算法:

增广路算法,用于计算二分图最大匹配。

感觉上面的定义都奇奇怪怪,这里又有一个小链接(这个博客里说的话是我能看得懂的了)

 

所以这玩意儿的主要过程大致为一个(bool类型)dfs(节点)如果返回值是true,就说明可以匹配,反之,不能:

dfs(x){

遍历与点x相连的边的另一个端点A,如果这个端点A没有被访问过的话,就执行下一行的操作:

(如果该端点A已经匹配过了,并且dfs A端点的匹配点 的返回值是true;或者端点A并没有被访问过),那么更新A的匹配点为x并返回true值结束dfs。

如果遍历完后都没有结束dfs的话,就返回false。

}

所以最大匹配的边数就是dfs(左部节点1~n)=true的个数。

这里是可可爱爱的代码实现!!!

板子是这个链接

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int N=100009;
 5 int tot,n,m,e;
 6 int to[N],nex[N],head[N],match[N],vis[N];
 7 
 8 void add(int u,int v){
 9     to[++tot]=v;nex[tot]=head[u];
10     head[u]=tot;
11 }
12 
13 bool dfs(int x){
14     for(int i=head[x];i;i=nex[i]){
15         int y=to[i];
16         if(!vis[y]){
17             vis[y]=1;
18             if(!match[y]||dfs(match[y])){
19                 match[y]=x;return true;
20             }
21         }
22     }
23     return false;
24 }
25 
26 signed main(){
27     cin>>n>>m>>e;
28     for(int i=1;i<=e;i++){
29         int x,y;
30         cin>>x>>y;
31         add(x,y+n);
32     }
33     int ans=0;
34     for(int i=1;i<=n;i++){
35         memset(vis,0,sizeof(vis));
36         if(dfs(i)) ans++;
37     }
38     cout<<ans;
39     return 0;
40 }

由于每一个左部节点都最多遍历完一次二分图,假设左部节点个数为N,右部节点个数为M,所以该算法的时间复杂度为O(NM)。

THE END...

posted @ 2021-02-06 09:41  001A  阅读(117)  评论(0编辑  收藏  举报