uoj79 一般图最大匹配&【带花树】初学

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

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值