[转]带花树,Edmonds's matching algorithm,一般图最大匹配
看了两篇博客,觉得写得不错,便收藏之。。
首先是第一篇,转自某Final牛
带花树……其实这个算法很容易理解,但是实现起来非常奇葩(至少对我而言)。
比如昨晚找到一篇鄙视带花树的论文,然后介绍了一种O(E)的一般图最大匹配……我以为找到了神论文,然后ACM_DIY众神纷纷表示这个是错的……于是神论文成为了”神论文“……
又比如围观nocow上带花树标程,一看……这显然是裸的匈牙利算法……货不对板啊
当然……如果二分图的匈牙利算法还不会请先围观求二分图最大匹配的匈牙利算法。
实际上任意图求最大匹配也是找增广路,但是由于奇环的出现,找增广路变得困难。
首先明确一点,增广路上是不能有重复出现的点的。
二分图中,匹配边可以看作是有向的,比如定义总是从X集指向Y集。假若定义了起点必须在X集中,那么增广路中出现该匹配边时,必然是按照这个方向的。所以一个点在增广路中的奇偶性是确定的。
而这个图中,从增广路3->1->4->5和2->4->1->6可以看出,对于有奇环的任意图,1和4这两个点在增广路中所在位置的奇偶性不再一定。于是我们考虑处理这些奇环。
定义奇环:包含2k+1个点和k条匹配边的一个环。(如果不是这样,我们找增广路不会走上去)
对于这个奇环,k条匹配覆盖了2k个点,那么显然有一个点未被覆盖。我们拿出这个点来讨论。

比如图中的1号点就是这个这个特殊的点。除了这个点以外,其它的点都被覆盖了,所以只能向外连非匹配边,而1号点可以向外连匹配边
或非匹配边。
如果1号点没有被外面的点匹配,那么无论从其它的哪个点走进来,都能以1为终点找到增广路。(要么顺时针跑到1,要么逆时针)
同理如果1号点被外面的点匹配了,那么无论从其它的哪个点走进来,都能把这个圈看成一个点,然后从1的那条匹配边穿出去。(要么顺时针,要么逆时针)
于是这个奇环就可以看成一个点,其主要特性由1号点体现(诸如和谁匹配了之流)。
这个合成点就叫做花。这个算法的思想就是不断地把奇环合成点,直至找到增广路(合成了某朵花以后就把整朵花当成一个点)。
考虑用BFS搜索增广路。
围观wiki这个图
由于BFS的性质,我们找到奇环只能是和同层的点,或者下下一层的点。
然后奇环的关键点必然是这棵BFS树里深度最浅的点。然后考虑合成以后,花如何展开对应的路径,使得我们能够增广。
花套花这个东西想起来都纠结>_<。
amber的程序里面并没有把点真的合成,只是弄了一个表示集合的标号:Base,然后邻接矩阵就不用变来变去了。
对于花中连向父亲的是匹配边的点,他的增广路显然是直接顺着父亲走,而如果连向父亲的边是非匹配边的点,那么显然是往后走然后跑过红色的横插边,然后再向上跑回关键点。
注意到如果连向父子的边是匹配边的点原先是不需要Father这个域来描述的,直接用表示匹配的那个域就可以了。但是现在在花中,他的Father这个域就要起作用了,用来向后指向,然后绕过红色横插边然后再跑回关键点。
实在是太精妙了。
//Problem:http://acm.timus.ru/problem.aspx?space=1&num=1099
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N=;
int n;
int head;
int tail;
int Start;
int Finish;
int link[N]; //表示哪个点匹配了哪个点
int Father[N]; //这个就是增广路的Father……但是用起来太精髓了
int Base[N]; //该点属于哪朵花
int Q[N];
bool mark[N];
bool map[N][N];
bool InBlossom[N];
bool in_Queue[N]; void CreateGraph(){
int x,y;
scanf("%d",&n);
while (scanf("%d%d",&x,&y)!=EOF)
map[x][y]=map[y][x]=;
} void BlossomContract(int x,int y){
fill(mark,mark+n+,false);
fill(InBlossom,InBlossom+n+,false);
#define pre Father[link[i]]
int lca,i;
for (i=x;i;i=pre) {i=Base[i]; mark[i]=true; }
for (i=y;i;i=pre) {i=Base[i]; if (mark[i]) {lca=i; break;} } //寻找lca之旅……一定要注意i=Base[i]
for (i=x;Base[i]!=lca;i=pre){
if (Base[pre]!=lca) Father[pre]=link[i]; //对于BFS树中的父边是匹配边的点,Father向后跳
InBlossom[Base[i]]=true;
InBlossom[Base[link[i]]]=true;
}
for (i=y;Base[i]!=lca;i=pre){
if (Base[pre]!=lca) Father[pre]=link[i]; //同理
InBlossom[Base[i]]=true;
InBlossom[Base[link[i]]]=true;
}
#undef pre
if (Base[x]!=lca) Father[x]=y; //注意不能从lca这个奇环的关键点跳回来
if (Base[y]!=lca) Father[y]=x;
for (i=;i<=n;i++)
if (InBlossom[Base[i]]){
Base[i]=lca;
if (!in_Queue[i]){
Q[++tail]=i;
in_Queue[i]=true; //要注意如果本来连向BFS树中父结点的边是非匹配边的点,可能是没有入队的
}
}
} void Change(){
int x,y,z;
z=Finish;
while (z){
y=Father[z];
x=link[y];
link[y]=z;
link[z]=y;
z=x;
}
} void FindAugmentPath(){
fill(Father,Father+n+,);
fill(in_Queue,in_Queue+n+,false);
for (int i=;i<=n;i++) Base[i]=i;
head=; tail=;
Q[]=Start;
in_Queue[Start]=;
while (head!=tail){
int x=Q[++head];
for (int y=;y<=n;y++)
if (map[x][y] && Base[x]!=Base[y] && link[x]!=y) //无意义的边
if ( Start==y || link[y] && Father[link[y]] ) //精髓地用Father表示该点是否
BlossomContract(x,y);
else if (!Father[y]){
Father[y]=x;
if (link[y]){
Q[++tail]=link[y];
in_Queue[link[y]]=true;
}
else{
Finish=y;
Change();
return;
}
}
}
} void Edmonds(){
memset(link,,sizeof(link));
for (Start=;Start<=n;Start++)
if (link[Start]==)
FindAugmentPath();
} void output(){
fill(mark,mark+n+,false);
int cnt=;
for (int i=;i<=n;i++)
if (link[i]) cnt++;
printf("%d\n",cnt);
for (int i=;i<=n;i++)
if (!mark[i] && link[i]){
mark[i]=true;
mark[link[i]]=true;
printf("%d %d\n",i,link[i]);
}
} int main(){
// freopen("input.txt","r",stdin);
CreateGraph();
Edmonds();
output();
return ;
}
然后还有一篇,链接请猛戳。。
在北京冬令营的时候,yby提到了“带花树开花”算法来解非二分图的最大匹配。
http://builtinclz.abcz8.com/art/2012/ural1099.cpp
没错,这是用来解决URAL 1099 Work Schedule那题的。时间复杂度是O(N^3)
[转]带花树,Edmonds's matching algorithm,一般图最大匹配的更多相关文章
- Augmenting Path Algorithm : 一般图最大匹配
算法原理详见 http://www.csie.ntnu.edu.tw/~u91029/Matching.html orz 带花树很神奇,挖坑最大权匹配 #include <iostream> ...
- HDU-4687 Boke and Tsukkomi 带花树,枚举
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4687 题意:给一个无向图,求所有的最大匹配的情况所不包含的边.. 数据比较小,直接枚举边.先求一次最大 ...
- kuangbin带你飞 匹配问题 二分匹配 + 二分图多重匹配 + 二分图最大权匹配 + 一般图匹配带花树
二分匹配:二分图的一些性质 二分图又称作二部图,是图论中的一种特殊模型. 设G=(V,E)是一个无向图,如果顶点V可分割为两个互不相交的子集(A,B),并且图中的每条边(i,j)所关联的两个顶点i和j ...
- HDOJ 4687 Boke and Tsukkomi 一般图最大匹配带花树+暴力
一般图最大匹配带花树+暴力: 先算最大匹配 C1 在枚举每一条边,去掉和这条边两个端点有关的边.....再跑Edmonds得到匹配C2 假设C2+2==C1则这条边再某个最大匹配中 Boke and ...
- URAL1099. Work Scheduling(一般图匹配带花树开花算法)
1099. Work Scheduling Time limit: 0.5 second Memory limit: 64 MB There is certain amount of night gu ...
- HDU 4687 Boke and Tsukkomi 一般图匹配,带花树,思路,输出注意空行 难度:4
http://acm.hdu.edu.cn/showproblem.php?pid=4687 此题求哪些边在任何一般图极大匹配中都无用,对于任意一条边i,设i的两个端点分别为si,ti, 则任意一个极 ...
- URAL 1099. Work Scheduling (一般图匹配带花树)
1099. Work Scheduling Time limit: 0.5 secondMemory limit: 64 MB There is certain amount of night gua ...
- HDU 4687 Boke and Tsukkomi (一般图匹配带花树)
Boke and Tsukkomi Time Limit: 3000/3000 MS (Java/Others) Memory Limit: 102400/102400 K (Java/Othe ...
- ZOJ 3316 Game 一般图最大匹配带花树
一般图最大匹配带花树: 建图后,计算最大匹配数. 假设有一个联通块不是完美匹配,先手就能够走那个没被匹配到的点.后手不论怎么走,都必定走到一个被匹配的点上.先手就能够顺着这个交错路走下去,最后一定是后 ...
随机推荐
- [Firefly引擎][学习笔记四][已完结]服务器端与客户端的通讯
原地址:http://www.9miao.com/question-15-54981.html 传送门:学习笔记一学习笔记二学习笔记三 前言:学习笔记三是模块封装,这个在持续开发中会不断更新, 因为写 ...
- 项目图片上传存储的目录部分代码思路Calendar类获取年月日
在项目中将产品图片全部放置在一个文件夹下面的话,如果图片很多的话,加载速度会减慢,可以按照文件夹按年,月,日来分开存放,图片文件名字取系统时间long类型加上5位随机数字码避免重复. Calendar ...
- Linux的分段和分页机制
1.分段机制 80386的两种工作模式 80386的工作模式包括实地址模式和虚地址模式(保护模式).Linux主要工作在保护模式下. 分段机制 在保护模式下,80386虚地址空间可达16K个段,每 ...
- 【Java】JTable组件的构造函数和设置列宽
使用JTable组件 类层次结构图: java.lang.Object --java.awt.Component --java.awt.Container --javax.swing.JCompone ...
- python学习笔记三--字典
一.字典: 1. 不是序列,是一种映射, 键 :值的映射关系. 2. 没有顺序和位置的概念,只是把值存到对应的键里面. 3. 通过健而不是通过偏移量来读取 4. 任意对象的无序集合 5. 可变长,异构 ...
- 抱怨IT公司人才缺乏?留住现有人才方是正途
摘要:员工的好坏决定着IT公司的未来,可很多IT公司在抱怨之时自己的人才却正在流失,如何留住现有的优秀员工?国外知名经理人Sharon Florentine建议:改善自己的管理.让员工不断地学习.创建 ...
- Hive QL 介绍
小结 本次课程学习了 Hive QL 基本语法和操作. 一.实验环境说明 1. 环境登录 无需密码自动登录,系统用户名shiyanlou,密码shiyanlou 2. 环境介绍 本实验环境采用带桌面的 ...
- JDK_Proxy_InvocationHandler_动态代理
本文用jdk动态代理模拟了spring的AOP的实现技术 AOP面向切面编程,可用于权限验证,效率检查,事务,异常管理等 JDK的动态代理主要涉及到java.lang.reflect包中的两个类:Pr ...
- bzoj4008
好题,这题可以分开算每个技能的贡献 orz popoqqq http://blog.csdn.net/popoqqq/article/details/45365759 ..,..] of double ...
- bzoj1858: [Scoi2010]序列操作
lazy-tag线段树. #include<cstdio> #include<algorithm> #include<cstring> using namespac ...
