uoj171 bzoj 4405: [wc2016]挑战NPC 一般图最大匹配

9 篇文章 0 订阅

       不得不说建图太巧妙了,我在知道是一般图最大匹配的情况下都不会建图。。。

       首先每个筐放三个,这个“三”是很关键的。

       把一个筐拆成三个点,然后每一个球向可以放的三个筐都连边;同时三个筐之间互相连边。那么如果一个筐只有<=1个点连向了它,那么一定会多一条匹配边;此外由于题中保证存在合法的放球的方案,因此每个点至少能连出一条边,因此基本的边数为N,那么最大匹配边数-N就是答案了。

       注意bzoj和uoj上面的输出是不一样的。

AC代码如下(uoj):

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 1005
#define M 250005
using namespace std;

int n,m,cnt,tot,head,tail,dfsclk,h[N],fst[N],pnt[M],nxt[M],fa[N],path[N],kind[N],match[N],pre[N];
void add(int x,int y){
	pnt[++tot]=y; nxt[tot]=fst[x]; fst[x]=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=pre[match[x]];
	}
}
void shrink(int x,int y,int k){
	while (getfa(x)!=k){
		pre[x]=y; int z=match[x];
		if (kind[z]==1){ kind[z]=0; h[++tail]=z; }
		if (getfa(x)==x) fa[x]=k;
		if (getfa(z)==z) fa[z]=k;
		y=z; x=pre[y];
	}
}
bool bfs(int sta){
	int i,x,y,z,p; for (i=1; i<=cnt; i++){ fa[i]=i; kind[i]=-1; }
	h[1]=sta; kind[sta]=0; head=0; tail=1;
	while (head<tail){
		x=h[++head];
		for (p=fst[x]; p; p=nxt[p]){
			y=pnt[p];
			if (kind[y]==-1){
				pre[y]=x; kind[y]=1;
				if (!match[y]){
					int now=y,last;
					for (; now; now=last){
						last=match[pre[now]];
						match[pre[now]]=now; match[now]=pre[now];
					}
					return 1;
				}
				kind[match[y]]=0; h[++tail]=match[y];
			}
			else if (!kind[y] && getfa(x)!=getfa(y)){
				z=lca(x,y); shrink(x,y,z); shrink(y,x,z);
			}
		}
	}
	return 0;
}
int main(){
	int cas; scanf("%d",&cas);
	while (cas--){
		scanf("%d%d%d",&n,&m,&cnt); int i,j,x,y;
		memset(fst,0,sizeof(fst)); tot=0;
		memset(match,0,sizeof(match)); memset(pre,0,sizeof(pre));
		for (i=1; i<=cnt; i++){
			scanf("%d%d",&x,&y);
			for (j=0; j<3; j++){
				add(x,n+3*y-j); add(n+3*y-j,x);
			}
		}
		for (i=1; i<=m; i++)
			for (x=0; x<3; x++) for (y=0; y<3; y++)
				if (x!=y) add(n+3*i-x,n+3*i-y);
		cnt=n+m*3; int ans=0;
		for (i=1; i<=cnt; i++)
			if (!match[i] && bfs(i)) ans++;
		printf("%d\n",ans-n);
		for (i=1; i<n; i++) printf("%d ",(match[i]-n-1)/3+1);
		printf("%d\n",(match[n]-n-1)/3+1);
	}
	return 0;
}


by lych

2016.3.25

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值