【总结】一般图最大匹配

二分图最大匹配:

首先我们回顾一下二分图最大匹配的算法:匈牙利算法
我们的方法是不停地寻找一条增广路,以增加我们的答案。其实带花树也是一样的思路,我们同样是寻找增广路。如果将匈牙利算法照搬到一般图上,我们会发现,算法是无法进行的。而问题出在奇环上。匈牙利算法,是基于反转操作的,每次找到一条增广路径,就将路径上所有的选择情况反转,以得到一个更大的匹配。但如果在一般图中,在增广路径走到奇环时,就会出现反转后矛盾的问题。
【总结】一般图最大匹配_第1张图片
如上图,虚线表示未选择的边,实线表示已选择的边,如果从A出发,可以找到一条增广路径:
A->B->C->D->E->C->B->F
的确这条路径是一条交错轨,但很容易发现:如果将每条边的选择情况反转,那么边DC与边CE就会冲突,边AB与边BF也会冲突。
注:这是论文上的图,但我个人认为这个图所反映的问题是不会出现的,因为我们在匈牙利算法中会标记访问,所以不可能走环,但似乎标记访问会造成其他问题。

如果是偶环,则不会对算法的正确性有影响(毕竟二分图中都可能出现偶环)。
所以,我们所有的问题就集中在了奇环上。

带花树算法:

虽然看名字,带花树很像是某种数据结构,但它并不是数据结构,而是一种类似于匈牙利算法的图论算法。它之所以称为“树”,是因为它可以将一个图看做一颗树的形式来解决问题,这一点上它又很类似于tarjan算法。

经过刚才的讨论,问题都集中在了奇环上,所以带花树最大的目标,就是解决奇环对算法的影响。

首先,我们仍然当做是二分图来做:
仍然是暴力找增广路径,对于我们枚举到的相邻点v
若v未访问过:
1、若v已经匹配,则从v开始继续bfs
2、若v未匹配,则找到一条增广路
若v访问过,则找到一个环:
1、若为偶环,直接忽略,跳过当前节点
2、若为奇环,则将当前的环暴力缩点,将环缩成一朵陈村

以下证明摘自论文(爱看不看):
【总结】一般图最大匹配_第2张图片
【总结】一般图最大匹配_第3张图片
【总结】一般图最大匹配_第4张图片
以上就是一般图最大匹配的带花树算法。
模板题:UOJ79

#include
#include
#include
#include
#include
#define SF scanf
#define PF printf
#define MAXN 510
using namespace std;
int mk[MAXN],fa[MAXN],nxt[MAXN],q[MAXN],vis[MAXN],match[MAXN];
int fr,bk,t,n,m;
vector<int> a[MAXN];
int find(int x){
    if(fa[x]==x)
        return x;
    fa[x]=find(fa[x]);
    return fa[x];
}
int LCA(int x,int y){
    t++;
    while(1){
        if(x){
            x=find(x);
            if(vis[x]==t)
                return x;
            vis[x]=t;
            if(match[x])
                x=nxt[match[x]];
            else
                x=0;
        }
        swap(x,y);
    }
}
void Union(int x,int y){
    if(find(x)!=find(y))
        fa[fa[x]]=fa[y];
}
void gr(int a,int p){
    while(a!=p){
        int b=match[a];
        int c=nxt[b];
        if(find(c)!=p)
            nxt[c]=b;
        if(mk[b]==2){
            q[++bk]=b;
            mk[b]=1;
        }
        Union(a,b);
        Union(b,c);
        a=c;
    }
}
void aug(int S){
    for(int i=1;i<=n;i++){
        mk[i]=nxt[i]=0;
        fa[i]=i;
    }
    mk[S]=1;
    fr=bk=0;
    q[fr]=S;
    while(fr<=bk){
        int x=q[fr++];
        for(int i=0;ix].size();i++){
            int y=a[x][i];
            if(match[x]==y)
                continue;
            else if(find(x)==find(y))
                continue;
            else if(mk[y]==2)
                continue;
            else if(mk[y]==1){
                int r=LCA(x,y);
                if(find(x)!=r)
                    nxt[x]=y;
                if(find(y)!=r)
                    nxt[y]=x;
                gr(x,r);
                gr(y,r);
            }
            else if(!match[y]){
                nxt[y]=x;
                for(int u=y;u;){
                    int v=nxt[u];
                    int mv=match[v];
                    match[u]=v;
                    match[v]=u;
                    u=mv;
                }
                return;
            }
            else{
                nxt[y]=x;
                mk[y]=2;
                q[++bk]=match[y];
                mk[match[y]]=1;
            }
        }
    }
}
int main(){
    SF("%d%d",&n,&m);
    int u,v;
    for(int i=1;i<=m;i++){
        SF("%d%d",&u,&v);
        a[u].push_back(v);
        a[v].push_back(u);
    }
    for(int i=1;i<=n;i++)
        if(!match[i])
            aug(i);
    int sum=0;
    for(int i=1;i<=n;i++)
        if(match[i])
            sum++;
    PF("%d\n",sum/2);
    for(int i=1;i<=n;i++)
        PF("%d ",match[i]);
}

但是,关于一般图最大匹配,还有一个算法:
其时间复杂度实测比带花树略慢,但代码相对容易实现

Tutte矩阵

(待补)

你可能感兴趣的:(图论)