二分图最大匹配 学习笔记

定义

二分图

二分图,又称二部图,英文名叫 Bipartite graph。
二分图是什么?节点由两个集合组成,且两个集合内部没有边的图。
——OI Wiki
显然我们发现,如果有一个二分图,我们可以把顶点染成两种颜色,存在一种方案使每一条边的两个顶点的颜色不同。
二分图有什么性质呢?显然我们发现二分图不存在长度为奇数的环。我们可以通过这一性质来检验一个图是否是二分图。

图匹配

假设图 G=(V,E) ,其中 V 是点集,E 是边集。
一组两两没有公共点的边集 M 称为这张图的匹配
定义匹配的大小为其中边的数量 ,其中边数最大的为最大匹配
当图中的边带权的时候,边权和最大的为最大权匹配

  • 无法再增加边的匹配称为 maximal matching ,它不一定是最大匹配。
  • 匹配边数最多的称为 maximum matching (最大匹配)。
  • 所有点都属于匹配称为 perfect matching (完美匹配),同时也符合最大匹配。
  • 发生在图的点数为奇数,刚好只有一个点不在匹配中,称为near-perfect matching(近完美匹配),扣掉此点以后的图称为 factor-critical graph。

算法解析

二分图最大匹配,顾名思义,就是求二分图中最大匹配的边数。我们可以通过网络流建模来解决这个问题。
为了方便叙述,我们把这个图分为两个点集 AB ,每个点集内无边

  1. 我们建立一个超级源和超级汇,将超级源联向集合 A 中所有点,容量为 1 ,将集合 B 的点全部连向超级汇,容量为 1
  2. 将图中的边都设为由点集 A 到点集 B ,容量为 1
  3. 求源点到汇点的最大流,即为答案。

算法复杂度分析:如果使用Dinic,不难发现最多增广 N 次,每次增广复杂度为 O(M) ,复杂度为 O(NM)
其中 N 为点数 M 为边数。
详细证明如下( from OI Wiki

Update on 2021.8.17

更改了代码,(原来写假了)。
更改的地方:在 dfs 函数增加了 if(!sum) break; 这句话。
代码:

#include<queue>
#include<cstdio>
#include<cstring>
#define maxn 10039
#define maxm 200039
#define min(a,b) ((a)<(b)?(a):(b))
using namespace std;
//#define debug
typedef int Type;
inline Type read(){
Type sum=0;
int flag=0;
char c=getchar();
while((c<'0'||c>'9')&&c!='-') c=getchar();
if(c=='-') c=getchar(),flag=1;
while('0'<=c&&c<='9'){
sum=(sum<<1)+(sum<<3)+(c^48);
c=getchar();
}
if(flag) return -sum;
return sum;
}
int n,m,e,s,t,u,v,w;
int head[maxn],to[maxm],nex[maxm],c[maxm],kkk=1,now[maxn];
#define add(x,y,z) to[++kkk]=y;\
nex[kkk]=head[x];\
now[x]=head[x]=kkk;\
c[kkk]=z;
int dep[maxn];
queue<int> q,E;
int bfs(){
memset(dep,0,sizeof(dep));
for(int i=1;i<=n+m+2;i++) now[i]=head[i];
q=E; q.push(s); dep[s]=1;
while(!q.empty()){
int cur=q.front(); q.pop();
for(int i=head[cur];i;i=nex[i])
if(!dep[to[i]]&&c[i]>0){
dep[to[i]]=dep[cur]+1;
if(to[i]==t)
return 1;
q.push(to[i]);
}
}
return 0;
}
int dfs(int x,int sum){
if(x==t) return sum;
int res=0,tmp;
for(int i=now[x];i&&sum>0;i=nex[i]){
now[x]=i;
if(dep[x]+1==dep[to[i]]&&c[i]>0){
tmp=dfs( to[i],min(c[i],sum) );
if(tmp==0) dep[to[i]]=0;
c[i]-=tmp; c[i^1]+=tmp; sum-=tmp; res+=tmp;
}
if(!sum) break;
}
return res;
}
int ans=0;
int main(){
//freopen("P3386_8.in","r",stdin);
//freopen(".out","w",stdout);
n=read(); m=read(); e=read();
for(int i=1;i<=e;i++){
u=read(); v=read();
add(u,n+v,1); add(n+v,u,0);
}
s=n+m+1; t=n+m+2;
for(int i=1;i<=n;i++){
add(s,i,1); add(i,s,0);
}
for(int i=n+1;i<=n+m;i++){
add(i,t,1); add(t,i,0);
}
while(bfs())
ans+=dfs(s,0x7fffffff);
printf("%d",ans);
return 0;
}
posted @   jiangtaizhe001  阅读(151)  评论(0编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示