带花树学习笔记

引入

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

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

理论

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

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

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

原因是二分图中可以保证增广的过程中匹配边都是从左边连到右边,但一般图中因为奇环的存在,使得这个方向是不定的。具体的原因不(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;通…

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;再次进入…

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

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

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;收到单个响…

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

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

Office 365开发者的前端必备课程

这篇文章其实不仅仅是写给Office 365开发者的&#xff0c;但对于广大的Office 365开发者来说确实相当重要的。这里提到的Office 365开发者&#xff0c;包括了Office Add-ins&#xff0c;SharePoint Add-ins&#xff0c;Microsoft Graph&#xff0c;Microsoft Teams的开发者。我…

【CF1338C】Perfect Triples【位运算】【构造】

传送门 题意&#xff1a;有一序列SSS由下列方式生成&#xff1a; 找到字典序最小的正整数(a,b,c)(a,b,c)(a,b,c)&#xff0c;满足a,b,ca,b,ca,b,c不在SSS中且a⊕b⊕c0a\oplus b\oplus c0a⊕b⊕c0,其中⊕\oplus⊕为异或将a,b,ca,b,ca,b,c加入SSS重复第一步 TTT组数据&#xff…

.NET开发框架(五)-IIS上部署ASP.NET Core项目教程

在之前教程中&#xff0c;我们分享了框架的功能与视频演示介绍(文尾底部提供往期教程快捷链接)系列教程&#xff1a;从初学者到架构师的一步步蜕变本篇经验将和大家介绍如何在IIS上部署ASP.NET Core项目&#xff0c;希望对初学.NET CORE的童靴入门有所帮助&#xff01;1、打开V…

P4146 序列终结者 平衡树 + lazy维护

传送门 文章目录题意&#xff1a;思路&#xff1a;题意&#xff1a; 思路&#xff1a; 平衡树裸题&#xff0c;直接维护俩lazylazylazy就行了。 需要注意的是&#xff0c;只有儿子节点存在的时候才能更新&#xff0c;不然更新到000号节点之后&#xff0c;给000号点加上了奇怪的…

.NET开发框架(四)-服务器IIS实践教程

前三篇教程中&#xff0c;我们分享了框架的功能与视频演示介绍(文尾底部提供往期教程快捷链接)&#xff0c;今天开始我们进入实践教程&#xff0c;从0开始教学&#xff0c;让你从新手到架构师之兑变&#xff0c;目前已经重置了一台服务器&#xff0c;从安装与配置各组件开始学习…

.NET Core IdentityServer4实战 第六章-Consent授权页

在identityServer4中登陆页面只要是成功了&#xff0c;就会注册一个Cookie在服务器资源上&#xff0c;像现在大部分的网站第三方授权&#xff0c;都是经过一个页面&#xff0c;然后选需要的功能&#xff0c;IdentityServer4也给我们提供了&#xff0c;只要你登陆成功,就会跳转到…

GitHub的CI实践(xUnit / OpenCover /Appveyor / Coveralls.net)

最近利用业余时间实现.ner core 版本的 casbin &#xff0c;即 Casbin.NET。之前的CI都使用的是公司搭建的jenkins和gitlab-runner&#xff0c;对开源社区的工具链并不是很熟悉&#xff0c;在casbin的原作者(hsluoyz )的“要求”下&#xff0c;只能被迫在项目的README.md加入下…