不得不说建图太巧妙了,我在知道是一般图最大匹配的情况下都不会建图。。。
首先每个筐放三个,这个“三”是很关键的。
把一个筐拆成三个点,然后每一个球向可以放的三个筐都连边;同时三个筐之间互相连边。那么如果一个筐只有<=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