带花树算法

对于一般的二分图匹配我们肯定会想到匈牙利算法,但是如果图中出现奇环怎么办?此时匈牙利算法就不可以了,就需要另一个算法:带花树算法
主要就是为了解决奇环的问题
我们匹配时会发现,如果存在奇环,传统的匈牙利算法在一个奇环里至少有一个点不能匹配,那么干脆就把这个奇环缩成一个点(也叫开花,这就是算法名字的由来),在处理到奇环的时候把它缩成一个点,路径取反的时候再暴力展开一个个取反。
缩点用并查集来维护

流程:

参考代码
我们给所有点黑白染色。假设开始增广的点是黑点。把所有黑点压进队列中顺次处理。对于一个黑点u,找与他相邻的点v,会出现一下四种情况:

1、u,v已经被缩成一个点了(这两个点在一朵花里),跳过。

2、v是白点,说明已经被匹配了,也就是偶环,跳过。

3、v还没有被染色。那就先把这个点染成白的,然后尝试与u匹配。如果v还没有匹配就匹配上,增广成功,然后沿增广路取反。如果v已经被匹配了,那么匹配他的点就是个黑点,染色,然后压进队列。

4、v也是黑点。这时候染色发生了冲突,说明遇见了奇环。这时候就需要找到两个点在带花树中的lca,然后把这整个环缩成一个点。(直接开花。)

缩点(开花)过程:
1、找x和y的LCA(的根)L。找LCA可以用各种方法。。。直接朴素也行。
2、在pre数组中把x和y接起来(表示它们形成环了!)
3、从x、y分别走到L,维护并查集使得变成一棵树,同时沿路把pre数组连接起来。

题目:

UOJ79 一般图最大匹配
从前一个和谐的班级,所有人都是搞OI的。有 n 个是男生,有 0 个是女生。男生编号分别为 1,…,n。
现在老师想把他们分成若干个两人小组写动态仙人掌,一个人负责搬砖另一个人负责吐槽。每个人至多属于一个小组。
有若干个这样的条件:第 v 个男生和第 u 个男生愿意组成小组。
请问这个班级里最多产生多少个小组?

题解:

就是带花树的模板题

代码:

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 510;
const int M = 3e5+10;
struct node{ int to,nxt; }g[M];
int head[N],cnt;
int vis[N],match[N],f[N],pre[N],Id,id[N];
//vis[i]: 0(未染色) 1(黑色) 2(白色)
//match[i]: i的匹配点
//f[i]: i在带花树中的祖先
//pre[i]: i的非匹配边的另一点 
//id: 找LCA用 
int n,m,ans,u,v;
queue<int> q;
void Init()
{Id=ans=cnt=0;for(int i=1;i<=n;i++)head[i]=-1,id[i]=match[i]=0;
}
void add(int u,int v){ g[cnt]=node{v,head[u]},head[u]=cnt++; }
int getf(int x){ return f[x]==x?x:f[x]=getf(f[x]); }
//查询x和y在带花树中的LCA 
int LCA(int x,int y)
{//沿着增广路向上找lca for(++Id;;swap(x,y))//x,y交替向上 if(x){x=getf(x);//有可能环中有环(花中有花),所以用并查集找祖先,只处理祖先节点 if(id[x]==Id) return x; //x,y在同一环中,一定会找到已被编号的点,该点即为LCA。 else id[x]=Id,x=pre[match[x]];//给点编号,并沿着非匹配边向上找		}
}
//缩点(开花 
void blossom(int x,int y,int l)//,将x、y到LCA(l)路径中的点,缩为一点int l)
{ while(getf(x)!=l){//增广路取反 pre[x]=y;y=match[x];//如果x、y的奇环中有白点,将其染为黑点,放入队列,让其去找不是环中的匹配点	if(vis[y]==2) vis[y]=1,q.push(y);//只改变是根的点 if(getf(x)==x) f[x]=l;if(getf(y)==y) f[y]=l;//增广路取反 x=pre[y];} 
}
bool aug(int s)
{//每次都以s为起点bfs,建带花树 for(int i=1;i<=n;i++)vis[i]=pre[i]=0,f[i]=i;	while(!q.empty()) q.pop();q.push(s),vis[s]=1;while(!q.empty()){u=q.front();q.pop();for(int i=head[u];~i;i=g[i].nxt){v=g[i].to;//如果已经在同一个环(花)中或者是白点(意为这已经有匹配点),只接跳过 //这种情况不会增加匹配数 if(getf(u)==getf(v)||vis[v]==2) continue;//如果没有被染色 if(!vis[v]){//先染为白色,将前继点指向u vis[v]=2;pre[v]=u;//如果没有被匹配过,直接匹配成功 if(!match[v]){//增广路取反 for(int x=v,last;x;x=last)last=match[pre[x]],match[x]=pre[x],match[pre[x]]=x;		return 1;}//如果被匹配过,则把匹配v的点染为黑色,放入队列中	vis[match[v]]=1;q.push(match[v]);}//v是黑色,形成奇环,则缩点(开花)。 else {int lca=LCA(u,v);blossom(u,v,lca);blossom(v,u,lca);}}	}return 0;
}
int main(void)
{while(~scanf("%d%d",&n,&m))	{Init();while(m--){scanf("%d%d",&u,&v);add(u,v),add(v,u);	}        for(int i=1;i<=n;i++)if(!match[i]&&aug(i))ans++;	printf("%d\n",ans);for(int i=1;i<=n;i++)printf("%d%c",match[i]," \n"[i==n]);}return 0;	
} 

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

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

相关文章

最短路径(状压dp)(ybtoj)

解析 “bug总有de完的一天” 头疼 暴力写的话本题显然复杂度是假的 有一个很好的思路优化时间复杂度 先用dp[k][i]表示**从第k个关键点到任意i点的最短路 跑k遍 SPFA或迪杰斯特拉 即可 然后转移时可以只考虑关键点 使状态转移数大大降低 细节 头真疼 边界条件&#xff1a; …

P3352-[ZJOI2016]线段树【dp】

正题 题目链接:https://www.luogu.com.cn/problem/P3352 题目大意 nnn个数字的一个序列&#xff0c;每次随机选择一个区间让这个区间所有数等于这个区间的最大值&#xff0c;重复qqq次&#xff0c;对每个位置求所有情况下这个位置的值的和。 1≤n,q≤4001\leq n,q\leq 4001≤n…

P2403 [SDOI2010]所驼门王的宝藏(强连通分量)(拓扑排序)

文章目录题目描述解析代码洛谷传送门题目描述 解析 看题目要求很容易想到强连通分量缩点加拓扑dp 但是问题在于存图 第一感就是和暴力和每个点连边 但那样无论点数和边数都很爆炸 随后我们发现这个图非常稀疏 所以我们可以只连有宝藏的点 然而这样边数会被一行横门这样的数据…

Xamarin.Forms之UserDialogs 重制版本

在 forms 里面&#xff0c;目前使用比较多的弹出组件是 Acr.UserDialogs &#xff0c;但是这个组件有些小问题&#xff0c;比如 loading .hide 会同时把 toast 给一起关掉&#xff0c;android 下的 toast 希望是 安卓原生的toast 样子&#xff0c;而不是 底部弹出一个横条&…

H.Minimum-cost Flow

H.Minimum-cost Flow 题目&#xff1a; 其实就是给出每条边的单位费用&#xff0c;q次查询&#xff0c;每次查询改变所有边的容量&#xff08;所有边容量一样&#xff09;&#xff0c;问最后流出1流量的最小花费是多少&#xff1f; 题解&#xff1a; 暴力做法肯定是每次询问…

P6177-Count on a tree II/[模板]树分块

正题 题目链接:https://www.luogu.com.cn/problem/P6177 题目大意 nnn个点的一棵树mmm次询问树上颜色。 强制在线 1≤n≤4105,1≤m≤105,0≤vali<2311\leq n\leq 4\times 10^5,1\leq m\leq 10^5,0\leq val_i<2^{31}1≤n≤4105,1≤m≤105,0≤vali​<231 解题思路 把所…

eShopOnContainers 看微服务③:Identity Service

引言通常&#xff0c;服务所公开的资源和 API 必须仅限受信任的特定用户和客户端访问。那进行 API 级别信任决策的第一步就是身份认证——确定用户身份是否可靠。在微服务场景中&#xff0c;身份认证通常统一处理。一般有两种实现形式&#xff1a;基于API 网关中心化认证&#…

P2168 [NOI2015] 荷马史诗(哈夫曼编码树)

传送门 文章目录题目描述解析细节代码题目描述 解析 其实就是构造一棵树 另所有带权点都为叶子节点 其代价为权值与深度的乘积 求最小代价及最小代价下的最小深度 可以看成一开始有n棵小树 每次把k棵合并在一起 最后合成一棵大树就行了 每次合并的代价是k棵树的权值和 看到这…

B-Suffix Array

B-Suffix Array 题意&#xff1a; 一个字符串只含有a和b&#xff0c;先给出b数组的构造方式&#xff1a; 对于每个位置i来说&#xff1a; 如果存在一个位置j&#xff0c;使得j<i,且s[j] s[i],则b[i]i-j否则b[i]0 现在对字符串每个后缀都构造B数组&#xff0c;并按照字典…

Wannafly挑战赛10F-小H和遗迹【Trie,树状数组】

正题 题目链接:https://ac.nowcoder.com/acm/contest/72/F 题目大意 nnn个字符串&#xff0c;包括小写字母和#\##。其中#\##可以替换为任意字符串。求有多少对字符串可能相同。 保证每个字符串至少有一个#\##。 2≤n≤500000,∑i1n∣si∣≤1062\leq n\leq 500000,\sum_{i1}^n…

线段树合并、分裂

基本概念&#xff1a; 如果需要维护许多个大小为 \(10^5\) 级别的多重集&#xff0c;可以看做给每一个多重集建立一棵线段树。线段树的合并、分裂就是多重集的累加、分开。 这里使用动态开点的方式存储线段树树。 如果一个节点为空&#xff0c;那么它的编号为 \(0\) 。 变量释义…

(神奇的)虚树(初步了解)

参考文献&#xff1a; 博文1 博文2 博文3 引入 在一类树上动态规划问题中,题目给出的询问往往包含树上的很多各节点,并保证总的点数规模小于某个值. 如果我们直接在整颗树上进行dp的话,时间复杂度与询问的次数有关,这显然是不可接受的,如果我们可以找到一种动态规划的方法,使…

P2048 [NOI2010] 超级钢琴(RMQ 贪心)

文章目录题目描述解析代码传送门题目描述 解析 首先&#xff0c;如果只有一个和弦&#xff0c;那么问题显然简单了 用前缀和结合ST表随便做做即可 然而 这次要求前k大的 怎么办呢&#xff1f; 参照之前有一道序列合并的做法 我们想到&#xff0c;可以先建一个优先队列&#xf…

微服务架构基础之Service Mesh

ServiceMesh(服务网格) 概念在社区里头非常火&#xff0c;有人提出 2018 年是 ServiceMesh 年&#xff0c;还有人提出 ServiceMesh 是下一代的微服务架构基础。那么到底什么是 ServiceMesh&#xff1f;它的诞生是为了解决什么问题&#xff1f;企业是否适合引入 ServiceMesh&…

[SDOI2011]消耗战

[SDOI2011]消耗战 题意&#xff1a; 给出n个点的一棵带有边权的树,以及q个询问.每次询问给出k个点,询问这使得这k个点与1点不连通所需切断的边的边权和最小是多少. 题解&#xff1a; 树型dp虚树 dp[x]:切断x及其子树上询问点的最小代价 预处理出minv[pos]代表从11到pos路径…

【做题记录】CF1428E Carrots for Rabbits—堆的妙用

CF1428E Carrots for Rabbits 题意&#xff1a; 有 \(n\) 个萝卜&#xff0c;每个萝卜的初始大小为 \(a_i\) 。现在要把这些萝卜切为为 \(k\) 个。吃每一个萝卜的时间为这个萝卜的大小的平方&#xff0c;求吃完所有萝卜的最小时间&#xff0c;即 \(\sum_{i1}^{k}{a_i^2}\) 最小…

P4716-[模板]最小树形图

正题 题目链接:https://www.luogu.com.cn/problem/P4716 题目大意 给出nnn个点mmm条边的一张有向图&#xff0c;求以rrr为根的最小外向树。 1≤n≤100,1≤m≤1041\leq n\leq 100,1\leq m\leq 10^41≤n≤100,1≤m≤104 解题思路 考虑一种贪心&#xff0c;对于每个点我们先选出…

P1081 [NOIP2012 提高组] 开车旅行(倍增)(动态规划)

洛谷传送门 文章目录题目描述解析代码题目描述 解析 利用倍增&#xff0c;设计dp慢慢敲即可。。。 注意距离累加在一起会爆int&#xff0c;需要ll 特判条件非常之复杂。。。 心力交瘁&#xff0c;就酱了 代码 #include <bits/stdc.h> using namespace std; #define ll…

dotnet core调试docker下生成的dump文件

最近公司预生产环境.net core应用的docker容器经常出现内存暴涨现象&#xff0c;有时会突然吃掉几个G,触发监控预警&#xff0c;造成容器重启。分析了各种可能原因&#xff0c;修复了可能发生的内存泄露&#xff0c;经测试本地正常&#xff0c;但是发到预生产还是会有内存暴涨现…

【做题记录】区间排序—线段树

1. CF558E A Simple Task 题意&#xff1a; 给定由小写字母组成的字符串 \(s\) 每一次操作如下&#xff1a; \(opt0\) &#xff1a;将 \([l,r]\) 降序排序 \(opt1\) &#xff1a;将 \([l,r]\) 升序排序 输出最终字符串 题解&#xff1a; 大致思想为&#xff0c;建 \(26\) 棵线…