二分图学习笔记

#二分图概念二分图是这样的一个图,它的顶点可以分为两个集合X和Y。所有的边关联的两个顶点中,恰好一个属于集合X,一个属于集合Y。二分图匹配给定一个二分图G,M为G边集的一个子集,如果M满足当中的任意两条边都不依附于同一个顶点,则称M是一个匹配。最大匹配图中包含边数最多的匹配称为图的最大匹配。完美匹配完美匹配: 如果所有点都在匹配边上,称这个最大匹配是完美匹配。二分图判定染色法,...
摘要由CSDN通过智能技术生成

#二分图

概念

二分图是这样的一个图,它的顶点可以分为两个集合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. 长度是奇数
  2. 第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是一条交替路。

匈牙利算法

步骤

  1. S = ∅ S = \emptyset S=,即所有的边均为非匹配边
  2. 寻找增广路 p a t h path path,把路径上的所有边的匹配状态取反,得到一个更大的集合 S ′ S' S.
  3. 重复2,直到不存在增广路

该算法关键是如何找到增广路。匈牙利算法依次尝试给每一个左边节点 x x x寻一个匹配的右边节点 y y y。右边节点y能与左边匹配,需要满足以下两个条件之一:

  1. y本身就是非匹配点。此时(x,y)本身就是非匹配边,自己构成长度为1的增广路
  2. 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还没匹配.于是我们找到了一个增广路径

所以总的流程可以如下:

  1. 对于一个未匹配的节点u,寻找它的每条边,如果它的边上的另一个节点v还没匹配则表明找到了一个匹配,直接转步骤4;
  2. 假如节点u它边上的另一个节点v已经匹配,那么就转向跟v匹配的节点,假设是w,然后再对w重复1,2的步骤,即寻找增广路
  3. 假如我们在1,2步过程中找到一条增广路,那么修改各自对应的匹配点,转4,若无增广路, 则退出.
  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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值