zoukankan      html  css  js  c++  java
  • 带花树学习笔记

    带花树学习笔记

    大部分内容参考 这篇博客 和 2015集训队论文。

    关于匹配

    定义翻转一条路径指的是将路径中所有匹配边变为非匹配边,非匹配边变为匹配边。

    定义一种路径是交错路径 (Alternating Path) 当且仅当路径是由匹配边和非匹配边交错产生。交错环 (Alternating Cycle) 同理。举例:

    image

    可以发现的一个性质是:交错环的所有边翻转后不影响匹配个数。

    定义一种路径是扩充路径 (Augmenting Path) 当且仅当它是一条交错路径且起始点和终止点都没有匹配。举例:

    image

    可以发现的一点是,如果存在扩充路径,那么翻转扩充路径可以使匹配 (+1)

    显然如果存在扩充路径,那么一定可以使匹配 (+1)。那么是不是不存在扩充路径就对应最大匹配呢?事实上这是可以证明的,只需要对非匹配点与非扩充路径的情况分类讨论即可。

    这样不断找到扩充路径,实际上就可以得到一个最大匹配。

    image

    定义交错树 (Augmenting Tree) 是以一个未匹配点作为树根,所有从树根出发的路径都是交错路径的树。

    当然路径总数会很大,事实上对于每条路径,我们只需要保留在任意一颗子树中即可。

    image

    可以证明这样总是正确的。

    关于二分图匹配

    如果给定一张二分图,如何求出最大匹配。

    二分图匹配的经典做法有匈牙利或者网络流,后者可以做到 (O(msqrt n)) 的复杂度。

    考虑二分图匹配为什么是对的。对于匈牙利算法来说,它的实质是找到一个未匹配点,然后遍历了以它为根的交错树,尝试搜索一条扩充路径更新。

    网络流算法则更加直接,利用二分图的性质钦定每条边的方向向右,钦定向左的边表示匹配边,向右的边表示非匹配边。直接套用网络流的反向弧来找到扩充路径。可以发现一条扩充路径恰好对应一次增流。

    关于一般图匹配

    如果这是一张二分图,那么上述问题可以通过匈牙利或者网络流算法做到非常好的复杂度。但如果这张图没有任何性质,由于网络流算法实际是利用二分图将无向边改成有向边,处理增广路径,这里就不大行了。

    但是能不能大力跑匈牙利处理呢。即每次查询一个点,如果它被匹配了就尝试更换它匹配的点的匹配边。但这样是有反例的:如果交错路径经过了某个奇环,那么翻转后会使得某一个点存在两个匹配。

    但是这个问题只会在奇环的时候出现。

    image

    可以发现如果存在这样的“花” (blossom),那么我们一定是从“花托” (base) 的位置进来的。可以证明在这种情况下,任何条经过花的扩充路径等价于缩完“花”之后的一条扩充路径。

    image

    注意这里的“花”只对当前扩充路径有效,所以当次扩充完需要重置。

    这样处理之后剩下的其实就是一张二分图,直接按类似于匈牙利的方式处理一下就行了。

    为了保证复杂度正确,使用 BFS 寻找扩充路径,如果遇到奇环就反复条父亲来找到“花托”。这样在一次扩充的过程中,一条边只会被访问一次,故复杂度为 (O(nmalpha(n)))

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<vector>
    #define N 20010
    using namespace std;
    int n,m;
    vector<int>g[N];
    namespace flower_tree{
        queue<int>q;
        int col[N],f[N];
        int find(int x){return f[x]==x?f[x]:(f[x]=find(f[x]));}
        int pre[N],lik[N];//pre:增广路 , lik:匹配
        int T,vis[N];
        int lca(int x,int y)
        {
            for(x=find(x),y=find(y),++T;vis[x]!=T;)
            {
                vis[x]=T;
                x=find(pre[lik[x]]);
                if(y) swap(x,y);
            }
            return x;
        }
        void flower(int x,int y,int v)
        {
            for(;find(x)!=v;x=pre[y])
            {
                pre[x]=y;y=lik[x];
                if(col[y]==2) col[y]=1,q.push(y);
                if(f[x]==x) f[x]=v;if(f[y]==y) f[y]=v;
            }
        }
        void clear(int n)
        {
            while(!q.empty()) q.pop();
            for(int i=1;i<=n;i++) f[i]=i,pre[i]=col[i]=0;
        }
        bool dfs(int s,int n)
        {
            clear(n);
            col[s]=1;q.push(s);
            while(!q.empty())
            {
                int u=q.front();q.pop();
                for(int v:g[u])
                if(find(u)!=find(v) && col[v]!=2)
                {
                    if(col[v])
                    {
                        int w=lca(u,v);
                        // if(w==0) throw;
                        flower(u,v,w);flower(v,u,w);
                        continue;
                    }
                    col[v]=2;pre[v]=u;
                    if(lik[v]) col[lik[v]]=1,q.push(lik[v]);
                    else
                    {
                        for(int x=v,y=lik[pre[x]];x;x=y,y=lik[pre[x]])
                            lik[x]=pre[x],lik[pre[x]]=x;
                        return true;
                    }
                }
            }
            return false;
        }
    }
    using flower_tree::dfs;
    using flower_tree::lik;
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            g[u].push_back(v);g[v].push_back(u);
        }
        int ans=0;
        for(int i=1;i<=n;i++)
        if(!lik[i]) ans+=dfs(i,n);
        printf("%d
    ",ans);
        for(int i=1;i<=n;i++) printf("%d ",lik[i]);
        return 0;
    }
    
  • 相关阅读:
    NOI2010 能量采集
    NOI2011 兔兔与蛋蛋游戏
    动态规划——min/max的单调性优化总结
    NOI2011 NOI嘉年华
    NOI2011 阿狸的打字机
    NOI2011 智能车比赛
    NOI2011 兔农
    NOI2012 魔幻棋盘
    NOI2012 美食节
    NOI2012 迷失游乐园
  • 原文地址:https://www.cnblogs.com/Flying2018/p/13429480.html
Copyright © 2011-2022 走看看