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

问题

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

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

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

  在一般图中,我们还是尝试使用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点就好)。

  代码中,用\(s[]\)记录标号。

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(){
    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;
}

原文地址:https://www.cnblogs.com/RogerDTZ/p/8571367.html

时间: 2024-08-30 00:14:58

带花树——一般图最大匹配的相关文章

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

一般图最大匹配带花树: 建图后,计算最大匹配数. 如果有一个联通块不是完美匹配,先手就可以走那个没被匹配到的点,后手不论怎么走,都必然走到一个被匹配的点上,先手就可以顺着这个交错路走下去,最后一定是后手没有路可走,因为如果还有路可走,这一条交错路,就是一个增广路,必然有更大的匹配. Game Time Limit: 1 Second      Memory Limit: 32768 KB Fire and Lam are addicted to the game of Go recently.

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

一般图最大匹配带花树+暴力: 先算最大匹配 C1 在枚举每一条边,去掉和这条边两个端点有关的边.....再跑Edmonds得到匹配C2 如果C2+2==C1则这条边再某个最大匹配中 Boke and Tsukkomi Time Limit: 3000/3000 MS (Java/Others)    Memory Limit: 102400/102400 K (Java/Others) Total Submission(s): 649    Accepted Submission(s): 202

poj 3020 一般图最大匹配 带花树开花算法

题意: 给出一个h*w的图,每个点都是'o'或'*',最少要用多少个1*2的矩形才能把图中所有的'*'都覆盖掉. 限制: 1 <= h <= 40; 1 <= w <= 10 思路: 最小边覆盖=|V|-最大匹配 一般图最大匹配,带花树开花算法 /*poj 3020 一般图最大匹配 带花树开花算法 题意: 给出一个h*w的图,每个点都是'o'或'*',最少要用多少个1*2的矩形才能把图中所有的'*'都覆盖掉. 限制: 1 <= h <= 40; 1 <= w &l

一般图最大匹配带花树

参考博客:http://blog.sina.com.cn/s/blog_95ec9e7401018bga.html https://www.cnblogs.com/owenyu/p/6858508.html 用Dinic实现的二分图匹配的时间复杂度其实是O(M*N^0.5),这也许能够解释为什么一般网络流算法比Hungry要快了. 另外,带花树算法的正确性的证明比较困难:而其时间复杂度是可以做到O(M*N^0.5)的 简述一下“带花树”算法吧: 它的核心思想还是找增广路.假设已经匹配好了一堆点,

【UOJ 79】 一般图最大匹配 (?带花树开花)

从前一个和谐的班级,所有人都是搞OI的.有 n 个是男生,有 0 个是女生.男生编号分别为 1,-,n. 现在老师想把他们分成若干个两人小组写动态仙人掌,一个人负责搬砖另一个人负责吐槽.每个人至多属于一个小组. 有若干个这样的条件:第 v 个男生和第 u 个男生愿意组成小组. 请问这个班级里最多产生多少个小组? 输入格式 第一行两个正整数,n,m.保证 n≥2. 接下来 m 行,每行两个整数 v,u 表示第 v 个男生和第 u 个男生愿意组成小组.保证 1≤v,u≤n,保证 v≠u,保证同一个条

kuangbin带你飞 匹配问题 二分匹配 + 二分图多重匹配 + 二分图最大权匹配 + 一般图匹配带花树

二分匹配:二分图的一些性质 二分图又称作二部图,是图论中的一种特殊模型. 设G=(V,E)是一个无向图,如果顶点V可分割为两个互不相交的子集(A,B),并且图中的每条边(i,j)所关联的两个顶点i和j分别属于这两个不同的顶点集(i in A,j in B),则称图G为一个二分图. 1.一个二分图中的最大匹配数等于这个图中的最小点覆盖数 König定理是一个二分图中很重要的定理,它的意思是,一个二分图中的最大匹配数等于这个图中的最小点覆盖数.如果你还不知道什么是最小点覆盖,我也在这里说一下:假如选

HDU 4687 Boke and Tsukkomi 一般图匹配,带花树,思路,输出注意空行 难度:4

http://acm.hdu.edu.cn/showproblem.php?pid=4687 此题求哪些边在任何一般图极大匹配中都无用,对于任意一条边i,设i的两个端点分别为si,ti, 则任意一个极大匹配中都必然有si或ti至少一个点被匹配,当在图中去掉si,ti两个点时,匹配数会损失一个或两个. 如果损失两个,就说明在极大匹配中这两个点分别连接不同的边,于是边i是无用的 所以总体思路:一般图匹配求出最大匹配数cnt0,分别试着去掉每条边的端点,再次匹配,匹配数如果小于cnt0-1,则这条边无

HDU 4687 Boke and Tsukkomi(一般图匹配|带花树)

比赛的时候刚开始看这题还以为是二分图匹配,后来才发现根本不是,因为该题存在长度为奇数的圈 .  比如1->2,2->3,3->1 . 所以该题要用一般图匹配,即带花树算法 . 比赛时抄的模板有地方抄错了,上述样例出现了死循环 .   赛后补题的时候用map去重却得不到正确答案,不知为何,暂放 ,下面给出一种正确解法. 细节参见代码: #include<cstdio> #include<cstring> #include<iostream> #inclu

URAL 1099. Work Scheduling 一般图匹配带花树

一般图匹配带花树模版题: 将奇环缩成圈(Blossom),然后找增广路..... 1099. Work Scheduling Time limit: 0.5 second Memory limit: 64 MB There is certain amount of night guards that are available to protect the local junkyard from possible junk robberies. These guards need to sche