带花树学习笔记

引入

带花树解决的是一般图的最大匹配问题。

学习此算法前你需要掌握二分图匹配的匈牙利算法,至少需要知道它的思想。

理论

众所周知,二分图匹配的思想是不断地找增广路。

严谨地讲,是找到了一条简单路径,它的起点和终点都未匹配,并且路径上的边是 匹配边-非匹配边 相互交错。

但是在一般图中直接找增广路会出锅。

原因是二分图中可以保证增广的过程中匹配边都是从左边连到右边,但一般图中因为奇环的存在,使得这个方向是不定的。具体的原因不(lan)再(de)说明,自己画个图就可以看出。

我们注意到,二分图和一般图最本质,或者说唯一的区别就是是否存在奇环。

注:对于这个奇环虽然不一定简单,但直接把它当成简单环处理,后面会详细说明。

我们发现对于一个大小为2k+12k+12k+1的奇环,从任意一个点都可以向外匹配,并且环内剩下的2k2k2k个点可以组成kkk对匹配。

这么说的话,环上的每个点都是等效的。

这么说我们可以直接把这2k+12k+12k+1个点缩成一个点处理,我们把缩成的点称为"花"。

然后继续增广就可以了。

流程

前面说着简单其实全在口胡……本算法的难点全在实现上。

记录每个点的匹配点mim_imi,如果没有匹配mi=0m_i=0mi=0,显然初始时mi=0m_i=0mi=0

首先枚举所有结点,当发现一个未匹配点(即mi=0m_i=0mi=0)时,尝试从这个点为起点找一条增广路。

设这个点为sss,从sss为根开始做 BFS 建出一棵带花树。注意带花树是对一个根单独建的,也就是每次都要清空数据。

因为我们要找出增广路翻转,对每个结点iii记录一个前驱 preipre_iprei,表示如果以sss为增广路起点,iii为终点,路径上的倒数第二个点(也就是iii往上跳一个点)是哪个。

值得注意的是,找增广路的时候并不是一直跳preipre_iprei,因为增广路是交替的,跳一步之后下一步一定是匹配边。

所以跳一步preprepre后直接走到对应的匹配点,即不断地i←mpreii \leftarrow m_{pre_i}imprei,这在后面将很有用。

对点进行染色。一个点有三种状态:黑色,白色,未染色。开始时所有点都未染色。然后起点sss设为黑色。

每次从队列中取出一个点uuu,从后面的流程可以知道它一定是黑点。

访问所有与uuu相邻的点vvv,然后大力分类讨论。

  1. uuuvvv在一个花中(也就是被缩成了一个点)

不知道咋处理,但脑电波一下应该没啥影响,所以跳过好了。

  1. vvv是白色

似乎还是没啥用,跳过好了。

  1. vvv未染色

如果 vvv没有被匹配,那么我们就找到了一条增广路,跳preprepre翻转所有边的匹配状态,答案+1+1+1,退出函数。

否则我们把vvv染成白色,并令prev=upre_v=uprev=u。因为vvv已经有匹配了,我们把mvm_vmv染成黑色并压进队列,表示从可以这里开始增广。

  1. vvv是黑色

最复杂的情况。当找到一个黑色的时候说明出现了一个奇环。

因为是 bfs,我们可以暴力跳preprepre找到以sss为根时uuuvvv的 LCA ,记为ppp

我们需要把这条路径上的点合并。

可以用并查集维护,把路径上的点并查集的父亲设为ppp就可以了。注意花里面可能套了花,所以只考虑并查集的根。

然而还没完,因为你还要求出具体的匹配点,所以你需要维护环内的匹配情况。

在这里插入图片描述

图例:粗边为匹配边,细边为未匹配边,箭头为preprepre

为了贯彻落实“任何一个点都可以向外匹配”的性质,盯着这个图,发现任何一个黑点(即所有匹配边的下面那个)在外面有匹配边的时候,都可以向上整一条增广路出来。

vvv为例:

在这里插入图片描述我们想让白点(匹配边上面那个)也可以整一个出来,不难构造出

在这里插入图片描述

地 狱 绘 图

这样利用找增广路是隔一次跳一步的性质,白点会从下面绕一圈上去,完美地解决了这个问题。具体实现见代码。

然后在跳的过程中把白点染成黑点并入队,表示欢迎外面的点进来找增广路。

如果你不知道为什么原来就是黑色的点不入队,想想你把它染成黑色的时候在干什么。

总复杂度是O(n3)O(n^3)O(n3),实际上很松,可以当O(n2)O(n^2)O(n2)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cctype>
#include <queue>
#define MAXN 1005
#define MAXM 100005
using namespace std;
inline int read()
{int ans=0;char c=getchar();while (!isdigit(c)) c=getchar();while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();return ans;
}
struct edge{int u,v;}e[MAXM];
int head[MAXN],nxt[MAXM],cnt;
void addnode(int u,int v)
{e[++cnt]=(edge){u,v};nxt[cnt]=head[u];head[u]=cnt;
}
int mat[MAXN],pre[MAXN],col[MAXN],fa[MAXN],n,m;
int idx[MAXN],tot;
inline int find(const int& x){return x==fa[x]? x:fa[x]=find(fa[x]);}
queue<int> q;
inline int lca(int x,int y)
{for (++tot;;swap(x,y))if (x){x=find(x);if (idx[x]==tot) return x;idx[x]=tot,x=pre[mat[x]]; }
}
inline void shrink(int x,int y,int l)
{while (find(x)!=l){pre[x]=y,y=mat[x];if (col[y]==2) col[y]=1,q.push(y);if (x==find(x)) fa[x]=l;if (y==find(y)) fa[y]=l;x=pre[y];}
}
inline int bfs(int s)
{while (!q.empty()) q.pop();q.push(s);col[s]=1;for (int i=1;i<=n;i++) pre[i]=col[i]=0,fa[i]=i;while (!q.empty()){int u=q.front();q.pop();for (int i=head[u];i;i=nxt[i]){int v=e[i].v;if (col[v]==2||find(u)==find(v)) continue;if (!col[v]){col[v]=2,pre[v]=u;if (!mat[v]){for (int last;v;v=last)last=mat[pre[v]],mat[v]=pre[v],mat[pre[v]]=v;return 1;}col[mat[v]]=1,q.push(mat[v]);}else{int l=lca(u,v);shrink(u,v,l),shrink(v,u,l);}}}return 0;
}
int main()
{n=read(),m=read();for (int i=1;i<=m;i++){int u,v;u=read(),v=read();addnode(u,v),addnode(v,u);}for (int i=1;i<=n;i++) fa[i]=i;int ans=0;for (int i=1;i<=n;i++)if (!mat[i])ans+=bfs(i);printf("%d\n",ans);for (int i=1;i<=n;i++) printf("%d%c",mat[i]," \n"[i==n]);return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/315064.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

你竟然没用 Nuget 构建项目?

想写这篇文章缘起于公众号文章里的一条留言&#xff1a;对于一个现代化的开发平台&#xff0c;建立一种让开发者创建&#xff0c;分享与使用可复用代码的机制是十分必要的。这种“可复用代码”被打包后的文件通常被称作“包”&#xff08;package&#xff09;&#xff0c;对于.…

Codeforces Round #339 (Div. 1) D. Kingdom and its Cities 虚树 + dp

传送门 文章目录题意&#xff1a;思路&#xff1a;题意&#xff1a; n≤1e5,∑k≤1e5,q≤1e5n\le1e5,\sum k\le1e5,q\le1e5n≤1e5,∑k≤1e5,q≤1e5。 思路&#xff1a; 经过分析&#xff0c;外敌占领的城市只有可能是两点的lcalcalca或者两点之间任意一点&#xff0c;是哪个点…

小白开学Asp.Net Core《四》 —— 使用AspectCore-Framework

小白开学Asp.Net Core《四》—— 使用AspectCore-Framework一、AspectCore-Framework说AspectCore-Framework不得不先谈谈的AOP&#xff0c;AOP&#xff1a;在软件业&#xff0c;AOP为Aspect Oriented Programming的缩写&#xff0c;意为&#xff1a;面向切面编程&#xff0c;通…

【WC2016】挑战NPC 【带花树】【建图】

传送门 题意&#xff1a;有nnn个球和mmm个筐&#xff0c;每个筐最多放333个球&#xff0c;每个球只能放入特定的一些筐中&#xff0c;在题中给出。构造一种放球的方案使得nnn个球都被放在某个筐中且 球的个数不超过111 的筐的数量尽量大。 m≤100,n≤3mm\leq 100,n\leq 3mm≤1…

P4103 [HEOI2014]大工程 虚树 + dp

传送门 文章目录题意&#xff1a;思路&#xff1a;题意&#xff1a; 思路&#xff1a; 简化一下题意就是求树上给定点集中每两个点之间的距离之和&#xff0c;相距最远的点&#xff0c;相距最近的点。 对于距离和我们统计一下边的贡献就好啦&#xff0c;边两头的sizesizesize乘…

【翻译】Keras.NET简介 - 高级神经网络API in C#

用C#&#xff0c;远离996Keras.NETKeras.NET是一个高级神经网络API&#xff0c;它使用C#编写&#xff0c;并带有Python绑定&#xff0c;可以在Tensorflow、CNTK或Theano上运行。其关注点是实现快速实验。因为做好研究的关键是&#xff1a;能在尽可能短的时间内从一个想法发展出…

TIOBE 6月排行:C# 以微弱的优势超过了 Visual Basic .NET 的排名,再次进入 TOP 5

月经贴警告……TIOBE 编程语言排行榜 7 月更新已公布&#xff0c;排名前十的分别是&#xff1a;Java, C, Python, C, C#, Visual Basic .NET, JavaScript, PHP, SQL 和汇编语言。和上个月的不同之处主要是 C# 以微弱的优势超过了 Visual Basic .NET 的排名&#xff0c;再次进入…

ETT学习笔记

ETT&#xff08;Eular Tour Tree&#xff09;是一种维护有根树的数据结构&#xff0c;支持以下操作 修改一个点的点权子树修改单点查询点到根路径查询修改一个点的父亲 据说可以支持换根&#xff0c;但用的不多而且据说很难写&#xff0c;所以似乎失传了&#xff08; 其实没…

Codeforces Round #635 (Div. 2) D. Xenia and Colorful Gems 暴力 + 二分

传送门 文章目录题意&#xff1a;思路&#xff1a;题意&#xff1a; 给你三个数组a,b,ca,b,ca,b,c&#xff0c;让你从每个数组中选择一个数x,y,zx,y,zx,y,z&#xff0c;使得(x−y)2(x−z)2(y−z)2(x-y)^2(x-z)^2(y-z)^2(x−y)2(x−z)2(y−z)2最小&#xff0c;求这个最小值。 …

用.NET Core实现一个类似于饿了吗的简易拆红包功能

需求说明以前很讨厌点外卖的我&#xff0c;最近中午经常点外卖&#xff0c;因为确实很方便&#xff0c;提前点好餐&#xff0c;算准时间&#xff0c;就可以在下班的时候吃上饭&#xff0c;然后省下的那些时间就可以在中午的时候多休息一下了。点餐结束后&#xff0c;会有一个好…

【POI2011】LIZ-Lollipop 【构造】

传送门 题意&#xff1a;给一个长度为nnn的只有111和222的序列&#xff0c;多次询问给定xxx构造或判断无法构造一个区间和为xxx 注意到111和222实质上是改不改变奇偶性&#xff0c;所以往这上面考虑 我们发现如果一个区间[L,R][L,R][L,R]和为x(x>2)x(x>2)x(x>2),我…

kubernetes实战篇之helm示例yaml文件文件详细介绍

前面完整示例里,我们主要讲解helm打包,部署,升级,回退等功能,关于这里面的文件只是简单介绍,这一节我们详细介绍一下这里面的文件,以方便我们参照创建自己的helm chart.Helm Chart 结构Chart 目录结构mychart/ Chart.yaml LICENSE README.md values.yaml requirements.yam…

P4309 [TJOI2013]最长上升子序列 平衡树 + dp

传送门 文章目录题意&#xff1a;思路&#xff1a;题意&#xff1a; 思路&#xff1a; 注意到一个很关键的条件&#xff0c;每次插入iii&#xff0c;而iii是递增的&#xff0c;也就是说插入iii之后只会从前面的最大值转移过来&#xff0c;所以我们现在只需要维护插入操作即可&…

【AGC035C】Skolem XOR Tree【异或】【构造】

传送门 题意&#xff1a;给定nnn&#xff0c;构造或判断无法构造一个2n2n2n个结点的树&#xff0c;其中结点iii和ininin的权值为iii,且所有iii和ininin路径权值异或和等于iii。 注意到 2i⊕2i112i\oplus2i112i⊕2i11&#xff0c;然后可以脑补出 然而111没处理 发现1⊕2⊕301\…

ASP.NET Core 管道再探

几乎任何服务器端处理环境都有自己的直通组件管道&#xff0c;用于检查、重路由或修改传入请求和传出响应。经典 ASP.NET 围绕 HTTP 模块理念进行排列&#xff0c;而 ASP.NET Core 采用基于中间件组件的更现代的体系结构。最终目的是相同的 - 允许可配置的外部模块以请求&#…

GRPC与.net core

QQ讨论群&#xff1a;953553560正文系列章节GRPC与.net coreGRPC截止时间与元数据GRPC与netcore IdentityGRPC与netcore IdentityServer4概述GRPC的数据交互模式有&#xff1a;1.单项RPC&#xff0c;最简单的数据交换方式&#xff0c;客户端发出单个请求&#xff0c;收到单个响…

【Hitachi2020C】ThREE【构造】【二分图染色】

传送门 题意&#xff1a;给一棵nnn个结点的树&#xff0c;构造一个nnn阶排列ppp&#xff0c;使得所有距离为333的点对(i,j)(i,j)(i,j)满足pipjp_ip_jpi​pj​和pipjp_ip_jpi​pj​至少一个为333的倍数。 分析一下&#xff0c;这个条件等价于所有距离333的点对点权对三取模后不…

【HNOI2015】接水果【整体二分】【DFS序】【双区间转矩形】【扫描线】【树状数组】

传送门 题意&#xff1a;给定一个nnn个点的树&#xff0c;定义一个“盘子”为一个给定权值的路径&#xff0c;一个“水果”为一条路径&#xff0c;一个盘子可以接到水果当且仅当盘子的路径是水果的子路径。给出所有盘子和水果&#xff0c;对于每个水果求可以接它的盘子中第kik…

程序员修神之路--做好分库分表其实很难之一

点击上方“蓝字”带你去看小星星菜哥&#xff0c;领导让我开发新系统了这么说领导对你还是挺信任的呀~必须的&#xff0c;为了设计好这个新系统&#xff0c;数据库设计我花了好多心思呢做一个系统我觉得不应该从数据库入手&#xff0c;应该从设计业务模型开始&#xff0c;先不说…

【CF1311E】Construct the Binary Tree【增量构造】【复杂度证明】

题意&#xff1a;给定nnn和ddd&#xff0c;构造或判断无法构造一棵二叉树使得所有点的深度&#xff08;定义为到根距离&#xff09;之和为ddd。 n,d≤5000n,d\leq 5000n,d≤5000 显然可以算出有解的ddd的下界和上界&#xff0c;分别是完全二叉树和链的情况。下面会证明在这个范…