BZOJ 1305:dance跳舞(二分+最大流)
一次舞会有n个男孩和n个女孩。每首曲子开始时,所有男孩和女孩恰好配成n对跳交谊舞。每个男孩都不会和同一个女孩跳两首(或更多)舞曲。
有一些男孩女孩相互喜欢,而其他相互不喜欢(不会“单向喜欢”)。每个男孩最多只愿意和k个不喜欢的女孩跳舞,而每个女孩也最多只愿意和k个不喜欢的男孩跳舞。
给出每对男孩女孩是否相互喜欢的信息,舞会最多能有几首舞曲? Input 第一行包含两个整数n和k。以下n行每行包含n个字符,其中第i行第j个字符为'Y'当且仅当男孩i和女孩j相互喜欢。 Output 仅一个数,即舞曲数目的最大值。 Sample Input 3 0 YYY YYY YYY Sample Output 3 Hint N<=50 K<=30
思路:二分答案,然后最大流。
建图:对于每个男生,拆成两个点A,B(而且A给B分流,流量为K):A用来连接喜欢的女生,B用来连接不喜欢的女生。很明显,二分到num时,给个A拥有num的流量,B拥有K流量。
同理:女生那里也要拆点。并且,男生流出的流和女生进入的流都要加num的限制。
#include<cstdio> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> using namespace std; const int inf=10000000; const int maxn=40010; char mp[60][60]; int Laxt[maxn],Next[maxn],To[maxn],cap[maxn],cnt; int dis[maxn],vd[maxn],S,N,T,K; void add(int u,int v,int val) { Next[++cnt]=Laxt[u]; Laxt[u]=cnt; To[cnt]=v; cap[cnt]=val; } int sap(int u,int flow) { if(flow==0||u==T) return flow; int tmp,delta=0; for(int i=Laxt[u];i;i=Next[i]){ if(dis[u]==dis[To[i]]+1&&cap[i]>0){ tmp=sap(To[i],min(cap[i],flow-delta)); delta+=tmp; cap[i]-=tmp; cap[i^1]+=tmp; if(delta==flow||dis[S]>T+1) return delta; } } vd[dis[u]]--; if(vd[dis[u]]==0) dis[S]=T+2; vd[++dis[u]]++; return delta; } bool check(int num) { int res=0; cnt=1; for(int i=S;i<=T;i++) Laxt[i]=dis[i]=vd[i]=0; for(int i=1;i<=N;i++) { add(S,i,num); add(i,S,0); } //分散给男生 for(int i=1;i<=N;i++) { add(i,N+i,K); add(N+i,i,0); } //给不喜欢的分配名额 for(int i=1;i<=N;i++) for(int j=1;j<=N;j++) if(mp[i][j]=='Y'){ add(i,3*N+j,1);add(3*N+j,i,0); } //给喜欢的女生分配 else { add(N+i,2*N+j,1); add(2*N+j,N+i,0); }//给不喜欢的分配 for(int i=2*N+1;i<=2*N+N;i++){ add(i,N+i,K); add(T,i,0); } //汇聚到女生 for(int i=3*N+1;i<=3*N+N;i++){ add(i,T,num); add(T,i,0); } //汇聚到汇点 while(dis[S]<=T+1) res+=sap(S,inf); if(res==num*N) return true; return false; } int main() { scanf("%d%d",&N,&K); S=0; T=4*N+1; for(int i=1;i<=N;i++) scanf("%s",mp[i]+1); int L=0,R=N,Mid,ans=0; while(L<=R){ Mid=(L+R)>>1; if(check(Mid)) ans=Mid,L=Mid+1; else R=Mid-1; } printf("%d\n",ans); return 0; }
It is your time to fight!