上次听毛主力讲完课忘记写笔记了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