一般图最大匹配——带花树算法
一般图最大匹配问题
题意:给出一张无向图,求出最大匹配的匹配数。
我们学过,二分图最大匹配有个算法——匈牙利算法,其主要思想就是枚举起点,不断寻找增广路,然后把找出的路径匹配状态取反,即可多出一个匹配。
当这个图存在奇环时,就不是二分图了,也就不能使用匈牙利算法。
容易发现,我们的增广路径不能经过奇环,否则就不符合匹配的条件了。
奇环的情况非常复杂,如果仍使用
用
算法思想
采用
此时我们搜的增广路径经过了这个奇环,设增光路从这个奇环出来的点为
考虑把这个奇环上的点的
在更一般的情况,我们可能存在“奇环套奇环”的情况,考虑用一个并查集维护,设
为了简便处理更多情况,在一个奇环上,对于原来非匹配边上的两点
综上,对于处理一个奇环,我们分为两部分:
-
找
在搜索树上的 。 -
把奇环上的点的
值处理一下,把 的点加入队列并改为 。
ll lca(ll u,ll v) //找 LCA
{
++T; //第 T 次找 LCA
u=find(u); v=find(v);
while(dfn[u]!=T)
{
dfn[u]=T; //跳过的点打标记
u=find(pre[mch[u]]);
if(v) swap(u,v); //u,v 轮流向上跳
}
return u;
}
void shrink(ll u,ll v,ll rt) //更新奇环上的点的信息
{
while(find(u)!=rt)
{
pre[u]=v; v=mch[u]; //更新 vis=1 的点的 pre 值
if(vis[v]==2) //把 vis=2 的点加入队列并改成 1
{
vis[v]=1; q[++r]=v;
}
if(find(u)==u) d[u]=rt; // 把奇环里面的点的并查集信息更新
if(find(v)==v) d[v]=rt;
u=pre[v];
}
}
除了处理奇环,当我们找到增广路后,暴力更新路径:
void aug(ll u)
{
ll tmp;
while(u)
{
tmp=mch[pre[u]];
mch[u]=pre[u]; mch[pre[u]]=u;
u=tmp;
}
}
总代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll maxn=1e5+10;
ll n,m,u,v,ans,head[maxn],tot,pre[maxn],mch[maxn],q[maxn],l,r,vis[maxn],d[maxn],dfn[maxn],T;
struct edge
{
ll v,nxt;
}e[maxn];
void insert(ll u,ll v)
{
e[++tot]=(edge){v,head[u]};
head[u]=tot;
}
ll find(ll x)
{
if(d[x]==x) return x;
return d[x]=find(d[x]);
}
ll lca(ll u,ll v)
{
++T;
u=find(u); v=find(v);
while(dfn[u]!=T)
{
dfn[u]=T;
u=find(pre[mch[u]]);
if(v) swap(u,v);
}
return u;
}
void shrink(ll u,ll v,ll rt)
{
while(find(u)!=rt)
{
pre[u]=v; v=mch[u];
if(vis[v]==2)
{
vis[v]=1; q[++r]=v;
}
if(find(u)==u) d[u]=rt;
if(find(v)==v) d[v]=rt;
u=pre[v];
}
}
void aug(ll u)
{
ll tmp;
while(u)
{
tmp=mch[pre[u]];
mch[u]=pre[u]; mch[pre[u]]=u;
u=tmp;
}
}
ll bfs(ll s)
{
for(ll i=1;i<=n;i++) pre[i]=vis[i]=0, d[i]=i;
q[l=r=1]=s; vis[s]=1;
while(l<=r)
{
ll u=q[l++];
for(ll i=head[u];i;i=e[i].nxt)
{
ll v=e[i].v;
if(vis[v]==0)
{
pre[v]=u;
if(!mch[v])
{
aug(v);
return 1;
}
else
{
vis[v]=2; vis[mch[v]]=1;
q[++r]=mch[v];
}
}
else if(vis[v]==1&&find(u)!=find(v))
{
ll rt=lca(u,v);
shrink(u,v,rt);
shrink(v,u,rt);
}
}
}
return 0;
}
int main()
{
scanf("%lld%lld",&n,&m);
for(ll i=1;i<=m;i++)
{
scanf("%lld%lld",&u,&v);
insert(u,v);
insert(v,u);
}
for(ll i=1;i<=n;i++)
if(!mch[i]) ans+=bfs(i);
printf("%lld\n",ans);
for(ll i=1;i<=n;i++) printf("%lld ",mch[i]);
return 0;
}
出处:https://www.cnblogs.com/Sktn0089/p/17663264.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下