上次听毛主力讲完课忘记写笔记了o(╯□╰)o。。发现现在忘得差不多了。于是补一发。
对于一般图 ,最大匹配,采用和二分图一样的思路。就是从每一个点出发,看能不能找到一条新的增广路,如果有就増广。
我们定义两类点A,B,A类点可以向外面去找増广路。对于某一次尝试的源点S,将它定义为A点。然后用宽搜的框架来増广。假设我们现在在一个A类点x,找增广路。现在假设x连向了一个点y
1.y是一个没有定义过的点:
(1)y还没有匹配边,那么找到了一条增广路,更新匹配边并返回;
(2)y已经有了匹配边,那么我们将y定义为B类点,假设y匹配的点z,将z定义为A类点,进行増广;
2.y是一个A类点:
说明找到了一个奇环,那么将这个环上的点缩成一个点,将这个新点定义为A类点,然后将和环上面的点相连的边转化为和这个新点相连的边,进行増广。具体实现时,只需要把环上的点全部定义为A类点,运用一个数组pre[]表示在bfs树中某一个点的父亲。然后对于x,y,运用pre[]数组对它们的最近公共祖先z向上爬,把路径上经过的B类点变成A类点即可;
3.y是一个B类点,直接无视。
对于uoj79,直接跑裸的带花树即可,AC代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#define N 1005
using namespace std;
int n,m,dfsclk,head,tail,h[N],path[N],kind[N],fa[N],match[N],pre[N];
int tot,fst[N],pnt[300005],nxt[300005];
void add(int aa,int bb){
pnt[++tot]=bb; nxt[tot]=fst[aa]; fst[aa]=tot;
}
int getfa(int x){ return (x==fa[x])?x:fa[x]=getfa(fa[x]); }
int lca(int x,int y){
for (dfsclk++; ; swap(x,y)) if (x){
x=getfa(x);
if (path[x]==dfsclk) return x;
path[x]=dfsclk;
x=(match[x])?pre[match[x]]:0;
}
}
void shrink(int x,int y,int k){
while (getfa(x)!=k){
pre[x]=y; int z=match[x];
if (kind[z]==1){ h[++tail]=z; kind[z]=0; }
if (getfa(x)==x) fa[x]=k;
if (getfa(z)==z) fa[z]=k;
y=z; x=pre[y];
}
}
bool ok(int sta){
int i; for (i=1; i<=n; i++){ fa[i]=i; kind[i]=-1; }
head=0; tail=1; h[1]=sta; kind[sta]=0;
while (head<tail){
int x=h[++head],p;
for (p=fst[x]; p; p=nxt[p]){
int y=pnt[p];
if (kind[y]<0){
pre[y]=x; kind[y]=1;
if (!match[y]){
int last,now=y,tmp;
for (; now; now=last){
last=match[tmp=pre[now]];
match[now]=tmp; match[tmp]=now;
}
return 1;
}
kind[match[y]]=0; h[++tail]=match[y];
} else if (!kind[y] && getfa(x)!=getfa(y)){
int tmp=lca(x,y); shrink(x,y,tmp); shrink(y,x,tmp);
}
}
}
return 0;
}
int main(){
scanf("%d%d",&n,&m); int i,ans=0;
for (i=1; i<=m; i++){
int x,y; scanf("%d%d",&x,&y);
add(x,y); add(y,x);
}
for (i=1; i<=n; i++)
if (!match[i] && ok(i)) ans++;
printf("%d\n",ans); for (i=1; i<=n; i++) printf("%d ",match[i]);
return 0;
}
by lych
2016.2.26