二分图
二分图
这里有一个讲义链接
定义:
如果一张无向图的N个节点(N≥2)可以分成A,B两个非空集合,其中A∩B=Ø,并且在同一集合内的点之间都没有边相连,那么称这张无向图为一张二分图。A,B分别称为二分图的左部和右部。
二分图判定:
一张无向图是二分图,当且仅当图中不存在奇环(长度为奇数的环)。
实现判定的方法(染色法)
二分图最大匹配:
下边是一些奇奇怪怪的定义:
任意两条边都没有公共端点的边被称为图的一组匹配。二分图中包含边数最多的一组匹配被称为二分图的最大匹配。
对于任意一组匹配S,属于S的边被称为匹配边,与之对应的,不属于S的边被称为非匹配边。匹配边的端点被称为匹配点,相反,其他节点被称为非匹配点。如果在二分图中存在一条连接两个非匹配点的路径path,使得非匹配边与匹配边在path上交替出现,那么称path是匹配S的增广路。
增广路具有下列性质:
- 长度len是奇数(因为以非匹配边起以非匹配边终)
- 路径上第奇数条边是非匹配边,第偶数条边是匹配边。
所以二分图的一组匹配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...