学习笔记——二分图及建图技巧

本文介绍了二分图的概念及其应用,重点讲解了匈牙利算法解决最大匹配问题,包括算法流程和复杂度优化。此外,还探讨了二分图建图的两种技巧:矩阵模型和记录匹配路径。最后,讨论了二分图最大独立集的求解方法,通过最大匹配数求解最大独立集,并介绍了不同场景下的建图策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

简介

二分图是一种特殊的图。其定义为:

节点由两个集合组成,且两个集合内部没有边的图。

那啥时候可以用二分图呢?当且仅当图可以进行合法的黑白染色的时候,可以考虑二分图。

二分图匹配

匈牙利算法

最基础的问题就是二分图的最大匹配。所谓最大匹配,就是对于左右两个集合内的点,每个点都只能选一次,并且左右匹配的点之间有连边,求最多能左右匹配的点对数。举个最简单的例子,就是有 n n n 个男生和 m m m 个女生,之间有相互喜欢的关系,求最终最多能牵几根红线。

由于这个问题比较基础,所以具体可以看网上其它博客,这里仅作简述。

我们常用的方法是匈牙利算法,这个算法,你可以感性地将之理解为一个有礼貌地戴绿帽的过程。

算法流程

  • 1 ∼ n 1\sim n 1n 便利每个男生。
  • 对于每个男生,先随便找一个两情相悦的女生。然后分两种情况:
    1 1 1、 这个女生没有匹配,那么就匹配上。
    2 2 2、 这个女生已经匹配了,那么就礼貌地问问那个匹配她的男生:“你好,我可以把你戴绿帽吗?”然后问题转化为那个可悲的被戴绿帽的男生去匹配其他女生的问题,可以递归解决。
  • 然后如果可以通过一连串的戴绿帽,就可以使答案加一。

这就是匈牙利的全过程,更具体的推荐扶咕咕的题解

Code

#include<bits/stdc++.h>
#define ll long long
#define pb push_back
#define inf 1<<30
using namespace std;
const int MAXN=510;
int n,m,e,mtc[MAXN];
bool used[MAXN],edge[MAXN][MAXN];
bool find(int x){
   
	for(int i=1;i<=m;i++){
   
		if(used[i]||!edge[x][i]) continue;
		used[i]=1;//标记在当前处理时,该女生是否访问。
		if(mtc[i]==0||find(mtc[i])){
   
			mtc[i]=x;
			return 1;
		}
	}return 0;
}
int main()
{
   
	scanf("%d%d%d",&n,&m,&e);
	for(int i=1,u,v;i<=e;i++){
   
		scanf("%d%d",&u,&v);
		edge[u][v]=1;
	}int ans=0;
	memset(mtc,0,sizeof(mtc));
	for(int i=1;i<=n;i++){
   
		memset(used,0,sizeof(used));
		if(find(i)) ans++;
	}printf("%d\n",ans);
}

Ps:一个优化
显然,匈牙利的复杂度是 O ( n 2 ) O(n^2) O(n2) 的,所以有人看到百万级别的题就不会往二分图匹配上去想了。这显然不对,因为有的题目每个点连出去的边只有几条,但是点却是上万的,此时,如果在遍历的时候用 v e c t o r vector vector 或者前向星存边,那么就可以只跑必定有边的匹配,可以大大降低复杂度。例如这道题,就可以这么做:

#include<bits/stdc++.h>
#define ll long long
#define pb push_back
#define inf 1<<30
#define INF 1ll<<60
using namespace std;
const int MAXN=1e6+10;
vector<int> e[MAXN];
int vis[MAXN],mtc[MAXN];
bool find(int x,int t){
   
	for(
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值