Live2d Test Env

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;
}

 

posted @ 2018-03-26 19:21  nimphy  阅读(204)  评论(0编辑  收藏  举报