二分图

定义

一张无向图,如果可以将节点分成两个部分,使得两个部分内部没有任何边相连,也就是每条边的端点都分属两个部分,就可以说这张图为二分图。

判定

当且仅当图中不存在长度为奇数的环时,这一张图为二分图。

因为显然每经过一条边都会到达另一个部分,以此类推,经过奇数条边后比不可能还在一开始的部分。

最大匹配

选择一些边,使得任意两条边都没有公共的端点,我们就可以称这些边为图的一组匹配,边数最多的一组我们称为最大匹配。

接下俩我们称被选中的边为匹配边,匹配边的端点为匹配点。如果二分图中存在一条路经连接两个非匹配点,使得匹配边与非匹配边在路径上交替出现,那么可以将此路径称为这组匹配的增广路。

根据定义可知,当我们将增广路上的匹配边变为非匹配边,非匹配边变为匹配边,新的匹配边仍然能够构成一组匹配,并且边数增加了1。

匈牙利算法:

用于计算二分图最大匹配,主要就是找增广路。算法会依次尝试为每一个二分图中的左部点\(x\)寻找一个匹配的右部点\(y\),流程如下:
从一个左端点出发,一次遍历它的出边,会到达一个点\(y\),接下来我们就要判断点\(y\)的情况:
1.\(y\)没有匹配点,那么可以将\(y\)d的匹配点变为\(x\)
2.\(y\)有匹配点\(z\),但是\(z\)可以寻找到新的匹配点。
对于点\(z\)我们可以重复上诉流程,知道寻找不到匹配点为止。

这样一来,如果一个点变为了匹配点,那么它永远不会变回非匹配点,即代表着,假如一个新的点变为了匹配点,那么总的匹配点数量肯定可以增加。

代码实现:模板题:P3386 【模板】二分图最大匹配

#include<algorithm>
#include<stdio.h>
#include<queue>
#define maxn 1005 
#define maxm 100005
using namespace std;
int n1,n2,m;
int head[maxn],to[maxm],nex[maxm],m1;
void add(int u,int v) {
	to[++m1]=v;
	nex[m1]=head[u];
	head[u]=m1;
}
int vis[maxn],match[maxn];
bool dfs(int now) {
	for(int i=head[now];i;i=nex[i]) {
		int st=to[i];
		if(!vis[st]) {
			vis[st]=1;
			if(!match[st] || dfs(match[st])) {
				match[st]=now; return true;
			} 
		}
	}
	return false;
}
int main() {
//	freopen("P3386.in","r",stdin);
//	freopen("P3386.out","w",stdout);
	scanf("%d%d%d",&n1,&n2,&m);
	for(int i=1;i<=m;i++) {
		int u,v; scanf("%d%d",&u,&v);
		add(u,v+n1),add(v+n1,u);
	}
	int ans=0;
	for(int i=1;i<=n1;i++) {
		for(int j=1;j<=n2+n1;j++) {
			vis[j]=0;
		}
		if(dfs(i)) ans++;
	}
	printf("%d\n",ans);
	return 0;
}

P1129 [ZJOI2007] 矩阵游戏

一道很不错的二分图最大匹配题。

首先,我们会发现两种操作其实只需要其中一种,如果只使用行操作,那么如果进行列操作,会出现类似拆东墙补西墙的情况,反过来同理。

所以我们考虑对于矩阵的每一个\((i,i)\),能否可以通过将行与行之间的位置调整来使每一个\((i,i)\)都为1。

我们会发现,如果此时对于第\(x\)行,如果它的第\(y\)列为1,那么它可以移动到第\(y\)行为\((y,y)\)提供贡献,这时我们可以认为第\(y\)行可以与第\(x\)行匹配。

也就是说,我们需要做出一种匹配方案,使得每一行都有对应唯一的匹配对象,此时就可以使用二分图最大匹配求解了。

代码

code
#include<algorithm>
#include<stdio.h>
#include<queue>
#define maxn 405
using namespace std;
int T;
int n;
int a[maxn][maxn];
int head[maxn],to[maxn*maxn],nex[maxn*maxn],m1;
void add(int u,int v) {
	to[++m1]=v;
	nex[m1]=head[u];
	head[u]=m1;
}
int vis[maxn],mat[maxn];
bool dfs(int now) {
	for(int i=head[now];i;i=nex[i]) {
		int st=to[i];
		if(!vis[st]) {
			vis[st]=1;
			if(!mat[st] || dfs(mat[st])) {
				mat[st]=now;
				return true;
			}
		}
	}
	return false;
}
int main() {
//	freopen("P1129.in","r",stdin);
//	freopen("P1129.out","w",stdout);
	scanf("%d",&T);
	while(T--) {
		scanf("%d",&n);
		m1=0;
		for(int i=1;i<=n*2;i++) {
			head[i]=0;
			mat[i]=0;
		}
		for(int i=1;i<=n;i++) {
			for(int j=1;j<=n;j++) {
				scanf("%d",&a[i][j]);
				if(a[i][j]) add(i,j+n),add(j+n,i);
			}
		}	
		int ans=0;
		for(int i=1;i<=n;i++) {
			for(int j=1;j<=n*2;j++) vis[j]=0;
			if(dfs(i)) ans++;
		}
		if(ans==n) printf("Yes\n");
		else printf("No\n");
	}
	 
	return 0;
}

posted @ 2023-08-17 15:59  Ian8877  阅读(7)  评论(0编辑  收藏  举报