二分图最大匹配 学习笔记
定义
二分图
二分图,又称二部图,英文名叫 Bipartite graph。
二分图是什么?节点由两个集合组成,且两个集合内部没有边的图。
——OI Wiki
显然我们发现,如果有一个二分图,我们可以把顶点染成两种颜色,存在一种方案使每一条边的两个顶点的颜色不同。
二分图有什么性质呢?显然我们发现二分图不存在长度为奇数的环。我们可以通过这一性质来检验一个图是否是二分图。
图匹配
假设图 ,其中 是点集, 是边集。
一组两两没有公共点的边集 称为这张图的匹配。
定义匹配的大小为其中边的数量 ,其中边数最大的为最大匹配。
当图中的边带权的时候,边权和最大的为最大权匹配。
- 无法再增加边的匹配称为 maximal matching ,它不一定是最大匹配。
- 匹配边数最多的称为 maximum matching (最大匹配)。
- 所有点都属于匹配称为 perfect matching (完美匹配),同时也符合最大匹配。
- 发生在图的点数为奇数,刚好只有一个点不在匹配中,称为near-perfect matching(近完美匹配),扣掉此点以后的图称为 factor-critical graph。
算法解析
二分图最大匹配,顾名思义,就是求二分图中最大匹配的边数。我们可以通过网络流建模来解决这个问题。
为了方便叙述,我们把这个图分为两个点集 和 ,每个点集内无边
- 我们建立一个超级源和超级汇,将超级源联向集合 中所有点,容量为 ,将集合 的点全部连向超级汇,容量为 。
- 将图中的边都设为由点集 到点集 ,容量为 。
- 求源点到汇点的最大流,即为答案。
算法复杂度分析:如果使用Dinic,不难发现最多增广 次,每次增广复杂度为 ,复杂度为 。
其中 为点数 为边数。
详细证明如下( 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; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· 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工具