二分图

定义

如果一张无向图可以分为两个集合,并且两集合内部的店没有边相连,则称该图为二分图。

判定

染色法

任意选择一个点开始染色,将与它相连的点染上相反的颜色,当出现矛盾时,说明不是二分图,否则为二分图。

例题:P1525 [NOIP2010 提高组] 关押罪犯

题目中将罪犯分为两组,同一监狱之间才会产生仇恨,将其抽象为图的边。如果将这些边删去,恰好为二分图。

求最大仇恨的最小值,设为 ans,显然可以二分。

我们将小于 ans 的边删去,保留大于 ans 的边,我们可以发现若此时图为二分图,则此 mid 成立,我们可以继续将 r 减小。

注意,图可能不连通。

时间复杂度为 \(O((n+m)\times \log max(c_i))\)代码:

#include<bits/stdc++.h>
using namespace std;
const int N = 2e4+5;
vector<pair<int,int> > e[N];
void add(int u,int v,int w){
	e[u].push_back(make_pair(v,w));
	e[v].push_back(make_pair(u,w));
}
int color[N];
int n,m,maxn = -1;
bool flag = true;
void dfs(int now,int now_color,int mid){
	if(flag == false){
		return;
	}
	color[now] = now_color;
	for(int i=0;i<e[now].size();i++){
		if(e[now][i].second <= mid){
			continue;
		}
		int v = e[now][i].first;
		if(color[v] == 0){
			dfs(v,3-now_color,mid);
		}else if(color[v] == now_color){
			flag = false;
		}
	}
	return;
}
bool check(int mid){
	for(int i=1;i<=n;i++){
		if(color[i] == 0){
			dfs(i,1,mid);
		}
	}
	return flag;
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
		int u,v,w;
		scanf("%d%d%d",&u,&v,&w);
		maxn = max(maxn,w);
		add(u,v,w);
	}
	int l = 0,r = maxn+1;
	while(l < r){
		flag = true;
		memset(color,0,sizeof color);
		int mid = (l+r)>>1;
		if(check(mid)){
			r = mid;
		}else{
			l = mid + 1;
		}
	}
	printf("%d",r);
}

二分图的最大匹配

定义

匹配:设 G 为二分图,若其子图 M 满足任意两条边之间无公共节点,则为二分图的匹配。

最大匹配:当 M 的边数最多时,为二分图的最大匹配。

匹配边:属于 M 的边,非匹配边同理。

交替路:从一个不属于 M 的点出发,依次经过不属于 M 的边、属于 M 的边……所形成的链为交替路。

增广路:从一个不属于 M 的点出发,走交替路,若能到达另一个不属于 M 的点,则经过的路径成为增广路。

增广路的性质

非匹配边数 - 匹配边数 = 1。

交换增广路的匹配边与非匹配边时,我们发现,匹配边多了一条,且仍然符合匹配的性质。

匈牙利算法

不断寻找增广路,将最广路上的匹配边与非匹配边互换,直到找不到增广路为止,用邻接表或动态数组存图时间复杂度为 \(O(nm)\),用邻接矩阵存图时间复杂度为 \(O(n^3)\)

模板

模板代码:

#include<bits/stdc++.h>
using namespace std;
const int N = 1e3+5;
vector<int> edge[N];
bool vis[N];
int match[N];
void add(int u,int v){
	edge[u].push_back(v);
}
bool dfs(int now){
	for(int i=0;i<edge[now].size();i++){
		int v = edge[now][i];
		if(vis[v]){
			continue;
		}
		vis[v] = true;
		if(!match[v] || dfs(match[v])){
			match[v] = now;
			return true;
		}
	}
	return false;
}
int main(){
	int n,m,e;
	scanf("%d%d%d",&n,&m,&e);
	for(int i=1;i<=e;i++){
		int u,v; 
		scanf("%d%d",&u,&v);
		add(u,v+n);
	}
	int ans = 0;
	for(int i=1;i<=n;i++){
		memset(vis,0,sizeof vis);
		if(dfs(i)){
			ans ++;
		}
	}
	printf("%d",ans);
}

2倍经验

例题:P1640 [SCOI2010] 连续攻击游戏

本题理论上匈牙利算法时间复杂度为 \(O(nm)\) 但实际上可以过。

本题理论正解为网络最大流,时间复杂度为 \(O(m\sqrt{n})\)

网络最大流

二分图的最大匹配可以转化为网络流模型。

posted @ 2023-11-15 17:10  WhileTureRP++  阅读(28)  评论(0)    收藏  举报