带花树

在此,借鉴了DY大神的代码传送门 Orz
还有一篇讲原理的博客,也写得好传送门Orz
总的来说带花树是用来进行一般图的最大匹配,
下面来解释一下带花树。
核心:找增广路。
具体操作:
(对于每一个搜出来的点都进行颜色的赋值)
1.增广路
假设已经匹配好了一堆点,我们从一个没有匹配的节点s开始,使用BFS生成搜索树。每当发现一个节点u,如果u还没有被匹配,那么就可以进行一次成功的增广;否则,我们就把节点u和它的配偶v一同接到树上,之后把v丢进队列继续搜索。
2.如果搜到了被访问过的点(其实就是遇到环了),那么就对环进行判断。
如果是偶环,即最后的点与出发的点颜色不相同,则无视那条边。
如果是奇环,即最后的点与出发的点颜色相同,就进行缩花(差不多就是缩点)。
3.缩花:
首先找x,y的LCA–p。(暴力也可以)
在Next[]中将x,y进行互连(形成一个环)。
在并查集中进行操作,将花中的每条边改成双向(无向图)。
同时将每个点的颜色都改为LCA的颜色。
4.继续以上操作,直到找不到增广路
以下代码为ural1099的代码题目
link[]代表它的配偶
G[]存图
Next[]它在增广路的前驱
fa[]并查集中的父亲
type[]是代表他的颜色
vis[]代表是否被访问过

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#define type Type
#define For(aa,bb,cc) for(int aa=bb;aa<=cc;++aa)
#define ak(aa) memset(aa,0,sizeof(aa))
#define next Next
using namespace std;
const int maxn=1010;
bool G[maxn][maxn],p[maxn];
int link[maxn],fa[maxn],next[maxn],type[maxn];
int n,ans;
queue<int>q;

/*带花树模板*/
struct Blossom_Tree{
    /*并查集维护*/
    int find(int x){
        return x==fa[x]?x:find(fa[x]);
    }
    /*
      将x和LCA连起来,
      将环中的所有颜色改成LCA的颜色
      将环中改为无向图,并改为同一个并查集
    */
    void combine(int x,int y){
        while(x!=y){
            int u=link[x],v=next[u];
            if(find(v)!=y) next[v]=u;
            if(type[u]==1) type[u]=2,q.push(u);
            fa[find(x)]=find(u),fa[find(u)]=find(v);
            x=v;
        }
        return ;
    }

    void contract(int x,int y){
        ak(p);
        int LCA=0;
        /*暴力LCA*/
        for(int i=x;i;i=next[link[i]]) i=find(i),p[i]=1;
        for(int i=y;i;i=next[link[i]]){
            i=find(i);
            if(p[i]){
                LCA=i;
                break;
            }
        }
        /*改双向边,且满足缩花的操作*/
        if(find(x)!=LCA) next[x]=y;
        if(find(y)!=LCA) next[y]=x;
        combine(x,LCA);
        combine(y,LCA);
        return ;
    }

    void bfs(int start){
        while(!q.empty()) q.pop();
        ak(type),ak(next);
        For(i,1,n) fa[i]=i;
        q.push(start),type[start]=1;
        while(!q.empty()){
            int k=q.front();q.pop();
            For(i,1,n){
                if(!G[k][i] || find(k)==find(i) || link[k]==i || type[i]==1) continue;
                /*不联通中,在同一个并查集中,互为配偶,偶环*/
                if(type[i]==2) contract(k,i);//奇环,缩花
                else if(link[i]){//将配偶加入队列
                    next[i]=k;
                    type[i]=1;
                    type[link[i]]=2;
                    q.push(link[i]);
                }else{//成功增广
                    next[i]=k;
                    int pos=i,u=next[pos],v=link[u];
                    while(pos){
                        link[u]=pos,link[pos]=u;
                        pos=v;
                        u=next[pos],v=link[u];
                    }
                    return ;
                }
            }
        }
        return ;
    }
}Tree;

int main(){
    scanf("%d",&n);
    int x,y;
    while(scanf("%d%d",&x,&y)!=EOF){
        G[x][y]=1,G[y][x]=1;
    }
    For(i,1,n){
        if(!link[i]) Tree.bfs(i);
    }
    For(i,1,n){
        if(link[i]) ++ans;
    }
    printf("%d\n",ans);
    For(i,1,n){
        if(link[i] && i<link[i]){
            printf("%d %d\n",i,link[i]);
        }
        link[i]=-1;
    }
    return 0;
}
发布了51 篇原创文章 · 获赞 6 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_35776579/article/details/54412344