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

问题

​ 给定一个图,求该图的最大匹配。即找到最多的边,使得每个点至多属于一条边。

​ 这个问题的退化版本就是二分图最大匹配。

​ 由于二分图中不存在奇环,偶环对最大匹配并无影响(可以调整)。所以增广路算法是可以顺利应用的。

​ 在一般图中,我们还是尝试使用BFS增广路的算法。

​ 然而一般图中还会出现奇环,在寻找增广路的时候,怎么处理奇环上的冲突?

​ 目的就是将奇环不断地缩起来(缩花),使得整个图在使用增广算法的时候不受影响,即不会经过奇环。

花

​ 一朵花由一个奇环缩点而成,一朵花里面可能还会有花。

​ 设这个奇环共有\(2k+1\)个点,那么在环内至多可以匹配到\(k\)条边,还会多出一个孤单的点。

​ 但是,形象地说,这个点可以在环里面自由移动。

​ 在图上将每个奇环缩成一个点成为一朵花,其实和原图是等价的,为什么?

​ 因为如果有合法增广路经过这朵花,在交替匹配边的时候,这朵花一定能通过那个自由点适应变化。

​ 画个图就明白了。

​ 使用并查集维护花,所有点的代表元指向这朵花里面在BFS树中深度最浅的点。

实现

​ 从每个未匹配的点开始进行BFS,找到一条合法增广路径以后,增广并退出。

​ 记这个未匹配的点为0类点,之后的点按10交替标序。

​ 每次在一个0点,枚举下一个点:

  1. 如果下一个点没有匹配,那么就找到了一个增广路,回溯并增广。

  2. 如果下一个点有匹配,那么就把它的匹配点加入队列中。

​

​ 设在搜索过程中,搜到连成环的边是\((u,v)\)。

​ 如果连成偶环,不需要理会;如果连城奇环,并且\(u\)和\(v\)不在一朵花内,就要对整个奇环缩花了。

​ 搜到奇环的时候,由于每次从0点枚举下一个点,\(u\)和\(v\)都是0点,环一定是这样的:

​ 首先要求出\(u\)和\(v\)的花意义下的\(lca\),它也是\(0\)点。做法是不断暴力向上跳,实际上是两个两个地跳。

​ 伪代码如下:

int getlca(int x,int y){
    clear visit[];
    x=find(x); y=find(y);
    while(1){
        if(x){
            if(x has been visited) return x;
            visit[x]=1;
            x=find(pre[match[x]])
        }
        swap(x,y);
    }
}

​ 其中\(match[x]\)记录的是\(x\)的匹配点,而\(pre\)记录的是每个1点的BFS父亲,\(find(x)\)返回\(x\)所属花的代表元。

​ 广义的讲,\(pre[x]\)的定义是如果\(x\)点失去了当前匹配点,那么它应该匹配谁。

​ 然后,对整个环缩花,从\((u,v)\)这条边向两边迭代。由于两边情况相同,一个函数调用两次即可:

int lca=getlca(u,v);
blossom(u,v,lca);
blossom(v,u,lca);

​ 首先是\(x\)和\(y\)的\(pre\)要互连,其次是把两个点的并查集的父亲设为\(lca\)(如果它是并查集的代表元,不是的话待会会遍历到的)。

​ 最后要将环中的1点全部扔进队列里,因为整个环缩起来了以后成为了一个点,要继续作为一个点寻找增广路,等价的做法就是把花里的所有点扔进队列(此时0点已经进过队列了所以不用扔);而缩起来的花是一个0点,故要将所有点的标号设为0(把1点设为0点就好)。

void blossom(int x,int y,int lca){
    while(find(x)!=lca){
        pre[x]=y;
        if(s[match[x]]==1){
            s[match[x]]=0;
            q.push(match[x]);
        }
        if(fa[x]==x) fa[x]=lca;
        if(fa[match[x]]==match[x]) fa[match[x]]=lca;
        y=match[x];
        x=pre[y];
    }
}

完整代码如下

#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int N=510,M=125000;
int n,m;
int h[N],tot;
int match[N],s[N],pre[N],vis[N],tim;
int fa[N];
queue<int> q;
struct Edge{int v,next;}g[M*2];
inline void addEdge(int u,int v){
    g[++tot].v=v; g[tot].next=h[u]; h[u]=tot;
    g[++tot].v=u; g[tot].next=h[v]; h[v]=tot;
}
inline int find(int x){return fa[x]==x?x:(fa[x]=find(fa[x]));}
int getlca(int x,int y){
    tim++;
    x=find(x); y=find(y);
    for(;;x^=y^=x^=y)
        if(x){
            if(vis[x]==tim) return x;
            vis[x]=tim;
            x=find(pre[match[x]]);
        }
}
void blossom(int x,int y,int lca){
    while(find(x)!=lca){
        pre[x]=y;
        if(s[match[x]]==1){
            s[match[x]]=0;
            q.push(match[x]);
        }
        if(fa[x]==x) fa[x]=lca;
        if(fa[match[x]]==match[x]) fa[match[x]]=lca;
        y=match[x];
        x=pre[y];
    }
}
int solve(int x){
    for(int i=1;i<=n;i++) fa[i]=i;
    memset(s,-1,sizeof s);
    memset(pre,0,sizeof pre);
    while(!q.empty()) q.pop();
    s[x]=0;
    q.push(x);
    while(!q.empty()){
        int u=q.front(); q.pop();
        for(int i=h[u],v;i;i=g[i].next){
            v=g[i].v;
            if(s[v]==-1){
                pre[v]=u;
                s[v]=1;
                if(!match[v]){
                    for(int go=1;go;v=go,u=pre[go]){
                        go=match[u];
                        match[u]=v; match[v]=u;
                    }
                    return 1;
                }
                s[match[v]]=0;
                q.push(match[v]);
            }
            else if(!s[v]&&find(u)!=find(v)){
                int lca=getlca(u,v);
                blossom(u,v,lca);
                blossom(v,u,lca);
            }
        }
    }
    return 0;
}
int main(){
    freopen("input.in","r",stdin);
    scanf("%d%d",&n,&m);
    int u,v;
    for(int i=1;i<=m;i++){
        scanf("%d%d",&u,&v);
        addEdge(u,v);
    }
    int ans=0;
    for(int i=1;i<=n;i++)
        if(!match[i])
            ans+=solve(i);
    printf("%d\n",ans);
    for(int i=1;i<=n;i++) printf("%d ",match[i]);
    return 0;
}

【Learning】带花树——一般图最大匹配的更多相关文章

  1. BZOJ 4405 [wc2016]挑战NPC 带花树 一般图最大匹配

    https://www.lydsy.com/JudgeOnline/problem.php?id=4405 这道题大概就是考场上想不出来,想出来也调不出来的题. 把每个桶拆成三个互相有边的点,每个球向 ...

  2. ZOJ 3316 Game 一般图最大匹配带花树

    一般图最大匹配带花树: 建图后,计算最大匹配数. 假设有一个联通块不是完美匹配,先手就能够走那个没被匹配到的点.后手不论怎么走,都必定走到一个被匹配的点上.先手就能够顺着这个交错路走下去,最后一定是后 ...

  3. 【learning】一般图最大匹配——带花树

    问题描述 ​ 对于一个图\(G(V,E)\),当点对集\(S\)满足任意\((u,v)\in S\),均有\(u,v\in V,(u,v)\in E\),且\(S\)中没有点重复出现,我们称\(S\) ...

  4. [转]带花树,Edmonds's matching algorithm,一般图最大匹配

    看了两篇博客,觉得写得不错,便收藏之.. 首先是第一篇,转自某Final牛 带花树……其实这个算法很容易理解,但是实现起来非常奇葩(至少对我而言). 除了wiki和amber的程序我找到的资料看着都不 ...

  5. HDOJ 4687 Boke and Tsukkomi 一般图最大匹配带花树+暴力

    一般图最大匹配带花树+暴力: 先算最大匹配 C1 在枚举每一条边,去掉和这条边两个端点有关的边.....再跑Edmonds得到匹配C2 假设C2+2==C1则这条边再某个最大匹配中 Boke and ...

  6. HDU 4687 Boke and Tsukkomi (一般图最大匹配)【带花树】

    <题目链接> 题目大意: 给你n个点和m条边,每条边代表两点具有匹配关系,问你有多少对匹配是冗余的. 解题分析: 所谓不冗余,自然就是这对匹配关系处于最大匹配中,即该匹配关系有意义.那怎样 ...

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

    <题目链接> <转载于 >>>  > 题目大意: 给出n个士兵,再给出多组士兵之间两两可以匹配的关系.已知某个士兵最多只能与一个士兵匹配.求最多能够有多少对匹 ...

  8. 【UOJ#79】一般图最大匹配(带花树)

    [UOJ#79]一般图最大匹配(带花树) 题面 UOJ 题解 带花树模板题 关于带花树的详细内容 #include<iostream> #include<cstdio> #in ...

  9. 【模板】一般图最大匹配(带花树算法)/洛谷P6113

    题目链接 https://www.luogu.com.cn/problem/P6113 题目大意 给定一个 \(n\) 个点 \(m\) 条边的无向图,求该图的最大匹配. 题目解析 二分图最大匹配,一 ...

随机推荐

  1. 织梦默认编辑器 按下回车生成br标签改为生成p标签

    找到文件 \include\ckeditor\config.js 把 config.enterMode = CKEDITOR.ENTER_BR; config.shiftEnterMode = CKE ...

  2. dedecms调用文章内容

    使用织梦建站时,有时候需要调用某一文档的内容,但织梦默认没有相应的标签,这时就需要我们使用sql语句去抓取了. {dede:sql sql="SELECT aid,typeid,body F ...

  3. sql server两个时间段内,求出周末的量

    公司有个表记录了出差(加班)的初始时间和截止时间,现在要计算出加班时间,之前的设计并没有考虑到这部分,因此本人通过sql重新计算周末数 表formmain starttime endtime 使用游标 ...

  4. .NET平台开源项目速览(19)Power BI神器DAX Studio

    PowerBI更新频繁,已经有点更不上的节奏,一直在关注和学习中,基本的一些操作大概是没问题,更重要的是注重Power Query,M函数,以及DAX的使用,这才是核心.   上个月研究了DAX的一些 ...

  5. bzoj 1188 [HNOI2007]分裂游戏 SG函数 SG定理

    [HNOI2007]分裂游戏 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 1394  Solved: 847[Submit][Status][Dis ...

  6. 分布式缓存一致性hash算法理解

    今天阅读了一下大型网络技术架构这本苏中的分布式缓存一致性hash算法这一节,针对大型分布式系统来说,缓存在该系统中必不可少,分布式集群环境中,会出现添加缓存节点的需求,这样需要保障缓存服务器中对缓存的 ...

  7. 使用FFMPeg对视频进行处理

    FFMPeg处理视频的核心操作方式是命令,无论是在Windows上还是Linux上.那么下边就简单介绍下,常见的处理命令. 示例1:截取一张352×240尺寸大小的,格式为jpg的图片: ffmpeg ...

  8. PHP实现水印效果(文字、图片)

    第一种 <?php /** * 功能:给一张图片加上水印效果 * $i 要加水印效果的图片 * $t 水印文字 * $size 文字大小 * $pos 水印的位置 * $color 文字的颜色 ...

  9. 1.1 Python是一种什么样的语言

    小时不识月,呼作白玉盘.很多人习惯地说Python不过是一种脚本语言而已,实际上这种说法是非常不准确的,完全不能体现出Python的强大.严格来说,Python是一门跨平台.开源.免费的解释型高级动态 ...

  10. 剑指offer第五天

    28.数组中出现次数超过一半的数字 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字.例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}.由于数字2在数组中出现了5次,超过数 ...