二分图最大匹配学习总结
二分图最大匹配学习总结
二分图的定义
如果无向图
图例:
匹配&最大匹配
匹配:给定一个图
最大匹配:在所有匹配中 边数最多的 一个匹配称为图的最大匹配。
最大匹配算法
一般图
一般图的最大匹配:带花树算法(Blossom Algorithm),高斯消元。
一般图的最大权匹配:带权带花树。
二分图
匈牙利算法
增广路径:若P是图G中一条连通两个 未匹配顶点 的路径,并且属匹配边集M的边和不属M的边(即已匹配和未匹配的边)在P上 交替出现,则称P为相对于M的一条增广路径。
算法过程:不断在图中寻找增广路径,每找到一条连接两个 未盖点 之间的增广路径,就将增广路径上的边 取反(将增广路径上 本属于匹配边的边 从最大匹配中 删除,本 不属于匹配边的边加入 最大匹配)。
由于增广路径的特殊性质,在每次取反操作后,最大匹配的边数会 加1。
证明:由于增广路径的两端点都是未盖点,所以连接两端点的边原先 都不属于最大匹配。所以,增广路径上本不属于最大匹配的边比原属于最大匹配的边数量 多1。
代码实现
#include<stdio.h>
#include<vector>
#include<cstring>
const int N=505;
std::vector<int> v[N];
int n,m,match[N],p[N];
bool vis[N];
bool dfs(int u){
for(auto i:v[u]){
if(!vis[i]){
vis[i]=true;
if(!match[i]||dfs(match[i])){
match[i]=u;
return true;
}
}
}
return false;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1,x;i<=n;i++){
scanf("%d",&x);
int y;
while(x--) scanf("%d",&y),v[i].emplace_back(y);
}
int ans=0;
for(int i=1;i<=n;i++){
std::memset(vis,false,sizeof vis);
ans+=dfs(i);
}
printf("%d",ans);
return 0;
}
最大流
由于每个点只会 匹配一个点,所以可以用最大流模型转化求解。
设一个超级源点
在跑最大流之前,我们再 把
最后使用最大流算法(如
注意:最大流在建边时需建一条 流量为
代码实现
#include<stdio.h>//Dinic
#include<algorithm>
#include<queue>
const int N=1e6+5,inf=0x3f3f3f3f;
struct jie{int to,val,nxt;}a[N];
int n,m,e,s,t,head[N],cnt=1,now[N],depth[N];
void add(int x,int y,int z){
cnt++;
a[cnt].to=y;
a[cnt].val=z;
a[cnt].nxt=head[x];
head[x]=cnt;
}
bool bfs(){
for(int i=1;i<=n+m+2;i++) depth[i]=inf;
std::queue<int> q;
depth[s]=0,now[s]=head[s],q.push(s);
while(!q.empty()){
int u=q.front();q.pop();
for(int i=head[u];i;i=a[i].nxt){
if(a[i].val>0&&depth[a[i].to]==inf){
now[a[i].to]=head[a[i].to],depth[a[i].to]=depth[u]+1,q.push(a[i].to);
if(a[i].to==t) return true;
}
}
}
return false;
}
int dfs(int u,int sum){
if(u==t) return sum;
int k,flow=0;
for(int i=now[u];i&&sum>0;i=a[i].nxt){
now[u]=i;
if(a[i].val>0&&depth[a[i].to]==depth[u]+1){
k=dfs(a[i].to,std::min(sum,a[i].val));
if(!k) depth[a[i].to]=inf;
a[i].val-=k,a[i^1].val+=k;
flow+=k,sum-=k;
}
}
return flow;
}
int Dinic(){
int ans=0;
while(bfs()) ans+=dfs(s,inf);
return ans;
}
int main(){
scanf("%d%d%d",&n,&m,&e);
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=1;i<=m;i++) add(i+n,t,1),add(t,i+n,0);
int x,y;
while(e--) scanf("%d%d",&x,&y),add(x,y+n,1),add(y+n,x,0);
printf("%d",Dinic());
return 0;
}
/*
How can I use Dinic to solve it?
e.g.:
scanf("%d%d%d",&n,&m,&e);
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=1;i<=m;i++) add(i+n,t,1),add(t,i+n,0);
int x,y;
while(e--) scanf("%d%d",&x,&y),add(x,y+n,1),add(y+n,x,0);
printf("%d",Dinic());
warning:the number of 'cnt' must start of 1
*/
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?