QAQ现在很不想写题解博客那就来写个算法吧QAQ...

带花树

题目

来看个题...

UOJ79.

某机房里有\(n\)个OIer,其中有\(n\)个男生,\(0\)个女生。现在他们要两两配对。

有\(m\)个关系,每个关系是一个无序对\((a_i,b_i)\),表示这两个人之间愿意配对。

求:最多能配成多少对,并找出一组方案。

说人话:一般图最大匹配。

算法

既然是匹配,我们能不能直接模仿匈牙利算法呢?答案是不可以(废话,可以的话还要什么带花树啊)。

原因是:我们在二分图中,如果dfs找增广路时经过某个点找不到,那么我们可以证明这一轮中这个点的确是无用的(也即,这一轮里所有的增广路都不经过这个点),于是我们就能保证每个点至多走一遍,时间复杂度得到保证。

但是如果是一般图,这个性质不一般成立。比如下图:

图中红线是已匹配的边。那么,如果我从\(1\)开始dfs时先经过\(2\),那么接下来就只能到\(4\)(因为只能走匹配边),然后是\(5,3\),同时我们会给这四个节点都打一个“找不到增广路”的标记。

但是实际上存在\(1\rightarrow3\rightarrow5\rightarrow4\rightarrow2\rightarrow6\)这条增广路。仔细观察我们就能发现:这种现象之所以存在,是由于奇环\(1-3-5-4-2-1\)的存在(如果没有奇环,就变成了二分图匹配,这时候匈牙利算法就是对的)。

那么,对于奇环,有什么后果呢?显然,如果我们dfs出了一个奇环,那么无论环上那个点dfs出了增广路都是可行的。(例如,上图中,如果从\(5\)处dfs出一条增广路,使得\(5\)匹配到别的点且\(3\)成为未盖点,那么可以走\(1-3\)。同样,如果\(5\)被孤立,我们可以走\(1-2-4-5\)来把\(5\)匹配上)

于是,我们可以把一个奇环当做一个点(这个奇环被称为“花”,这就是带花树名字的来历),然后继续找增广路。

形式化的,如果我们在图\(G=(V,E)\)中找到了一个奇环\(v_1-v_2-\dots-v_k-v_1\)(称为“花”),其中\(v_1\)是环上的深度最小的结点,不难证明,\(v_1\)的配偶不在此话中(因为在找到这个花之前所有边组成一个二分图,那么由于\(v_1\)向下dfs/bfs出了一个花,它一定是X结点,只有X结点会向外扩展),且\((v_2,v_3),(v_4,v_5)\dots(v_{k-1},v_{k})\)都是匹配边。那么我们构建一个图

\[\begin{aligned}G'&=(V', E'),\\ V'&=V/\{v_2,v_3\dots,v_k\},\\ E'&=\{(f(a),f(b))|(a,b)\in E, a,b\neq v_i, i=1\dots k\}\end{aligned}\]

其中\(f(v_i)=v_1, f(a)=a(a\neq v_j), i, j=1,2,\dots,k\)

并且原本\(G\)中的所有匹配除掉\((v_2,v_3),(v_4,v_5)\dots(v_{k-1},v_{k})\)构成\(G'\)的一个匹配。

那么,\(G\)中存在增广路\(\Leftrightarrow\)\(G'\)中存在增广路。

证明:

\(\Rightarrow\):对于\(G\)中的任意一条增广路,若其不经过这朵花,那么在\(G'\)中也存在这条增广路;否则,令这条从\(s\)开始的增广路上的最后一个在花上的点为\(v_j\),那么这条增广路形如 \(s\leadsto v_j \leadsto t\),我们在\(G'\)上构造如下增广路:先从\(s \leadsto v_1 \leadsto t\),其中第一段路程沿着bfs/dfs树走,第二段路程沿着原图中的增广路走,唯一不同的是\(v_j\)变成了\(v_1\)(这是合法的,因为所有从\(v_j\)出发的边都被连到了\(v_1\)上,而且我们根据所有\(v\)都是已盖点可以知道\(v_j\)出发的边是非匹配边)。

\(\Leftarrow\):对于\(G'\)中的一条增广路,若它不经过\(v_1\),则\(G\)中也存在;否则,设这条增广路为\(s\leadsto v_1\rightarrow x \leadsto t\)(\(x\)可能等于\(t\)),根据\(E'\)的定义存在\((v_i, x)\in E\),从而我们构造\(G\)中的增广路:\(s\leadsto v_1\leadsto v_i \rightarrow x \leadsto t\),其中第一段和第三段不变(因为增广路上\(v_1\)至多出现1次,所以这两段在\(G\)中存在),第二段是在花里走(或者精确一点,若\(i\)是奇数,走\(v_1\rightarrow v_2 \dots v_i\),否则走\(v_1\rightarrow v_k \dots v_i\)。证毕。

bfs时,我们可以\(O(n)\)求出LCA并\(O(kn)\)缩花,从而单次bfs至多\(O(n^2)\),总复杂度至多\(O(n^3)\)。

实现上,我们不实际缩点,而是对于每个点维护一个\(fa\),表示它所处的最大的花的LCA(就是\(v_1\))。由于花里可能还有花,这个\(fa\)要用并查集维护。在证明中构造增广路是通过判断\(i\)奇偶性,但实际上我们可以直接维护每个点要往哪边走,也即维护一个\(link_i\)表示如果\(i\)失配要和谁匹配(例如,\(link_{v_2}=v_1,link_{v_3}=v_4\))。找LCA的时候直接暴力\(O(n)\),但要注意只找每个并查集的根节点(因为非根节点都缩到花里了);缩花时要注意如果两个点已经在一朵花里就不要再缩了。

代码

#include <algorithm>
#include <cstdio>
const int N = 550;
const int M = 250050;
int pre[N], nxt[M], to[M], cnt, n;
int vis[N], fa[N], link[N], mate[N];
int que[N], head, tail;
int ss[N], time;
inline void addEdge(int x, int y) {nxt[cnt] = pre[x];to[pre[x] = cnt++] = y;
}
int Find(int x) { return fa[x] == x ? x : fa[x] = Find(fa[x]); }
int LCA(int x, int y) {++time;while (ss[x] != time) {if (x) {ss[x] = time;x = Find(link[mate[x]]);}std::swap(x, y);}return x;
}
void flower(int x, int y, int p) {while (Find(x) != p) {link[x] = y;fa[y = mate[x]] = fa[x] = p;if (vis[y] == 1)vis[que[tail++] = y] = 2;x = link[y];}
}
bool match(int x) {head = tail = 0;for (int i = 1; i <= n; ++i)vis[fa[i] = i] = 0;vis[que[tail++] = x] = 2;while (head != tail) {x = que[head++];for (int i = pre[x]; ~i; i = nxt[i]) {int u = to[i];if (!vis[u]) {vis[u] = 1;link[u] = x;if (mate[u])vis[que[tail++] = mate[u]] = 2;else {while (u) {x = mate[link[u]];mate[mate[u] = link[u]] = u;u = x;}return true;}} else if (vis[u] == 2 && Find(u) != Find(x)) {int p = LCA(x, u);flower(x, u, p);flower(u, x, p);}}}return false;
}
int main() {int m, x, y, ans = 0;scanf("%d%d", &n ,&m);std::fill(pre + 1, pre + n + 1, -1);while (m--) {scanf("%d%d", &x, &y);addEdge(x, y);addEdge(y, x);if (!mate[x] && !mate[y])mate[mate[x] = y] = x, ++ans;}for (int i = 1; i <= n; ++i)if (!mate[i] && match(i))++ans;printf("%d\n", ans);for (int i = 1; i <= n; ++i)printf("%d ", mate[i]);return 0;
}

转载于:https://www.cnblogs.com/y-clever/p/8084738.html

WC前的颓废——带花树相关推荐

  1. luogu P4258 [WC2016]挑战NPC(一般图的最大匹配,带花树,建图、拆点技巧)

    整理的算法模板合集: ACM模板 luogu P4258 [WC2016]挑战NPC 如果是一堆球一堆筐,每一个筐里只能放一个球,求最大能放多少个球, 那么就是一个二分图的最大匹配问题,非常简单,我们 ...

  2. 模板 - 一般图最大匹配(带花树)

    整理的算法模板合集: ACM模板 目录 题目描述 给出一张 n 个点 m 条边的无向图,求该图的最大匹配. 总结一下带花树算法的流程 1.每次找一个未匹配的点出来增广 2.在增广过程中,如果相邻点是白 ...

  3. luogu P6113 【模板】一般图最大匹配(带花树)

    整理的算法模板合集: ACM模板 总结一下带花树算法的流程 1.每次找一个未匹配的点出来增广 2.在增广过程中,如果相邻点是白点,或者是同一朵花中的节点,则直接跳过这个点 3.如果相邻点是一个未被匹配 ...

  4. 【BZOJ4405】【WC2016】挑战NPC(带花树)

    [BZOJ4405][WC2016]挑战NPC(带花树) 题面 BZOJ 洛谷 Uoj Description 小N最近在研究NP完全问题,小O看小N研究得热火朝天,便给他出了一道这样的题目: 有n个 ...

  5. 独家直播双十一全网动态?前黑客“劳改”带你玩转大数据

    独家直播双十一全网动态?前黑客"劳改"带你玩转大数据 发表于2015-11-24 10:26| 4044次阅读| 来源CSDN| 7 条评论| 作者蒲婧 CTO俱乐部CTOCTO讲 ...

  6. 【学习小记】一般图最大匹配——带花树算法

    Text 一般图的最大匹配仍然是基于寻找增广路的 增广路的定义是这样的一条路径,它不经过重复的点,并且路径两端均没有匹配,且整条路径是非匹配边-匹配边-非匹配边这样交错的. 类比二分图最大匹配的增广路 ...

  7. URAL - 1099 Work Scheduling(一般图最大匹配-带花树模板)

    题目链接:点击查看 题目大意:给出n个警卫,接下来给出数个关系,表示两个警卫可以互相配合,现在规定只有可以互相配合的警卫才能留下来继续工作,问最多能有多少个警卫留下来工作,输出匹配方案 题目分析:一般 ...

  8. 一般图最大匹配——带花树

    所谓花,就是如下图所示的一个奇环: 本文中粗边代表现在的匹配边,细边代表该点的前驱(后文会讲解前驱是什么,现在只需要知道每个点和它的前驱在原图中一定是有边的). 如图所示,一朵包含\(2k+1\)个点 ...

  9. 图论--一般带花树匹配

    带花树就是说一个非二分图,图中带有奇环的图,我们不能在奇环中找增广路,因为会陷入死循环,我们可以将带花树的花(奇环)部分缩成点处理,剩下的图就是一个无奇环的图.我们再找增广路,而奇环中的的点我们可以随 ...

最新文章

  1. linux 命令行叫dos,利用shell编程实现DOS风格的Linux命令行
  2. python编写单实例总结
  3. openssl下开发sm4-gcm-ciphers
  4. 业务逻辑 : forex mlm
  5. 7-1 图形卡片排序游戏 (40 分)
  6. 使用Stack进行递归
  7. input 输入速度和方向判断、搜索功能的延迟请求
  8. Java网络编程之通过代码实现Socket通信
  9. log以2为底的对数
  10. python:实现convolve卷积算法(附完整源码)
  11. SameSite 属性
  12. ABTest之最常见的八个错误
  13. 秒拍视频其实在你浏览器缓存里
  14. prometheus常用函数详解
  15. 苹果要加入Face ID到Mac,Magic Keyboard 添加 Touch Bar
  16. 是什么让区块链游戏变的“好玩”
  17. Ubuntu18.04编译c语言程序
  18. 海信智慧黑板Android版本,海信智慧黑板,给老师减负,再也不用“喝”粉笔末了...
  19. 西方经济学【微观】名词缩写整理
  20. Apollo 3.5硬件和系统安装指南

热门文章

  1. linux查看虚拟内存和cpu占用率
  2. 安装Exchange2013,FMS服务无法达到启动状态
  3. FireDAC 下的 Sqlite [4] - 创建数据库
  4. [软考]2013年系统架构设计师备考
  5. 正压防爆气体分析系统
  6. 中国民航局:2019年春运中国民航旅客运输量将创历史新高
  7. 我们在开源项目中是怎样埋彩蛋的
  8. MySQL/MariaDB基础及简单SQL语句
  9. 通过项目逐步深入了解Spring MVC(一)
  10. JAVA多线程之中断机制(如何处理中断?)