题解:

首先所有的球都得装进桶里,所以每个球都向可行的桶连边

一个桶最多可以装3个球,考虑把一个桶拆成三个点

现在要求半空桶的最大数量,如果一个桶只有一个点匹配,那么就cnt++

但是这样可能会有两个原本可以分开放的点匹配到同一个桶导致答案减小

有一种巧妙的建图方法,可以使能分开放的点尽量分开

把一个桶拆成的三个点两两连边

这样,在求最大匹配的情况下放在一起连的最大匹配数为3,而分开的最大匹配数为4

就可以解决最大化半空桶数的问题

如图:(红色为匹配边)

但是由于这样建出来的图并不是二分图,而是一般图

所以我们需要解决一般图的最大匹配问题

带花树

一个一般图如果带有奇环,那么它就一定不是一个二分图

假设图中存在这样一个奇环,它可能会贡献五条匹配边

但是它的内部最多只会贡献2条匹配边

此时最多只有一个外面的点能匹配奇环中的点

于是我们可以考虑把奇环缩成一个点来找增广路径

但是缩完点之后,本来可以匹配5条边的图就只存在了1+2条匹配

所有我们不能直接缩点,在dfs到该点的时候还需要对缩掉的点进行展开(开花)

这样再来求增广路径

下面一段引用自:https://www.cnblogs.com/owenyu/p/6858508.html

贴个模板:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 305
#define M 500005
int fir[N],to[M],nxt[M],cnt;
void adde(int a,int b)
{
	to[++cnt]=b;nxt[cnt]=fir[a];fir[a]=cnt;
	to[++cnt]=a;nxt[cnt]=fir[b];fir[b]=cnt;
}
namespace FT{
	int sz;
	int fa[N],pre[N],mat[N],vis[N];
	int q[N],he,ta;
	int tim[N],tm;
	int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
	int lca(int x,int y){
		for(tm++;;swap(x,y))if(x){
			x=find(x);
			if(tim[x]==tm)return x;
			tim[x]=tm;x=pre[mat[x]];
		}
	}
	void bls(int x,int y,int p){
		while(find(x)!=p){
			pre[x]=y;y=mat[x];
			if(vis[y]==2)vis[y]=1,q[++ta]=y;
			if(find(x)==x)fa[x]=p;
			if(find(y)==y)fa[y]=p;
			x=pre[y];
		}
	}
	bool bfs(int s){
		for(int i=1;i<=sz;i++)fa[i]=i;
		memset(vis,0,sizeof(vis));memset(pre,0,sizeof(pre));
		he=1;ta=0;q[++ta]=s;vis[s]=1;
		while(he<=ta){
			int u=q[he++];
			for(int v,p=fir[u];p;p=nxt[p]){
				if(vis[v=to[p]]==2||find(u)==find(v))continue;
				if(!vis[v]){
					vis[v]=2;pre[v]=u;
					if(!mat[v]){
						for(int tmp;v;v=tmp){
							tmp=mat[u=pre[v]];
							mat[v]=u;mat[u]=v;
						}
						return 1;
					}
					vis[mat[v]]=1;q[++ta]=mat[v];
				}
				else{
					int x=lca(u,v);
					bls(u,v,x);bls(v,u,x);
				}
			}
		}
		return 0;
	}
}//----FT----

 

本题代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 605
#define M 500005
int fir[N],to[M],nxt[M],cnt;
void adde(int a,int b)
{
	to[++cnt]=b;nxt[cnt]=fir[a];fir[a]=cnt;
	to[++cnt]=a;nxt[cnt]=fir[b];fir[b]=cnt;
}
int fa[N],pre[N],match[N],vis[N],q[N],he,ta;
int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
int sz,tim[N],tm;
int lca(int x,int y)
{
	for(tm++;;swap(x,y))if(x){
		x=find(x);
		if(tim[x]==tm)return x;
		else tim[x]=tm,x=pre[match[x]];
	}
}
void bls(int x,int y,int p)
{
	while(find(x)!=p){
		pre[x]=y;y=match[x];
		if(vis[y]==2)vis[y]=1,q[++ta]=y;
		if(find(x)==x)fa[x]=p;
		if(find(y)==y)fa[y]=p;
		x=pre[y];
	}
}
bool bfs(int s)
{
	for(int i=1;i<=sz;i++)fa[i]=i;
	memset(vis,0,sizeof(vis));memset(pre,0,sizeof(pre));
	he=1;ta=0;q[++ta]=s;vis[s]=1;
	while(he<=ta){
		int u=q[he];he++;
		for(int v,p=fir[u];p;p=nxt[p]){
			if(vis[v=to[p]]==2||find(v)==find(u))continue;
			if(!vis[v]){
				vis[v]=2;pre[v]=u;
				if(!match[v]){
					for(int tmp;v;v=tmp){
						tmp=match[u=pre[v]];
						match[v]=u,match[u]=v;
					}
					return 1;
				}
				vis[match[v]]=1;q[++ta]=match[v];
			}
			else{
				int x=lca(u,v);
				bls(u,v,x);
				bls(v,u,x);
			}
		}
	}
	return 0;
}
int main()
{
	int T,n,m,E,i,u,v;
	scanf("%d",&T);
	while(T--){
		memset(fir,0,sizeof(fir));cnt=0;
		memset(match,0,sizeof(match));
		scanf("%d%d%d",&n,&m,&E);
		sz=n+3*m;
		for(i=1;i<=E;i++){
			scanf("%d%d",&u,&v);
			adde(u,n+v);
			adde(u,n+m+v);
			adde(u,n+2*m+v);
		}
		for(i=1;i<=m;i++){
			adde(n+i,n+m+i);
			adde(n+m+i,n+2*m+i);
			adde(n+i,n+2*m+i);
		}
		int ans=0;
		for(i=1;i<=sz;i++)
			if(!match[i])ans+=bfs(i);
		printf("%d\n",ans-n);
		printf("%d",(match[1]-n-1)%m+1);
		for(i=2;i<=n;i++)
			printf(" %d",(match[i]-n-1)%m+1);
		printf("\n");
	}
}