您的位置:首页 > 其它

【总结】一般图最大匹配

2018-02-23 11:38 197 查看
二分图最大匹配:

首先我们回顾一下二分图最大匹配的算法:匈牙利算法

我们的方法是不停地寻找一条增广路,以增加我们的答案。其实带花树也是一样的思路,我们同样是寻找增广路。如果将匈牙利算法照搬到一般图上,我们会发现,算法是无法进行的。而问题出在奇环上。匈牙利算法,是基于反转操作的,每次找到一条增广路径,就将路径上所有的选择情况反转,以得到一个更大的匹配。但如果在一般图中,在增广路径走到奇环时,就会出现反转后矛盾的问题。



如上图,虚线表示未选择的边,实线表示已选择的边,如果从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、若为奇环,则将当前的环暴力缩点,将环缩成一朵陈村花

以下证明摘自论文(爱看不看):







以上就是一般图最大匹配的带花树算法。

模板题:UOJ79

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
#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;i<a[x].size();i++){
int y=a[x][i];
if(match[x]==y)
continue;
else if(find(x)==find(y))
continue;
else if(mk[y]==2)
conti
4000
nue;
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矩阵

(待补)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: