[BZOJ1305/Luogu3153][CQOI2009]dance跳舞

题目链接:

BZOJ1305

Luogu3153

看见匹配就上网络流

二分答案\(Mat\),把每个人拆成喜欢和不喜欢两个点。

对于男生,从源点向喜欢点连边,容量为\(Mat\),同时在喜欢和不喜欢之间连边,容量为\(k\)(因为总共只跳\(Mat\)次舞,要把两个点限制在一起)。

对于女生也是类似的反着,不喜欢\(\rightarrow\)喜欢,容量为\(k\),喜欢\(\rightarrow\)汇点,容量为\(Mat\)

然后对于两个人\((x,y)\),若\(x\)喜欢\(y\),在对于喜欢的点之间,容量为\(1\),否则连不喜欢。

然后用了个\(ISAP\)

#include <cstdio>
#include <cstring>

inline int Min(int a,int b){return a<b?a:b;}

int n,k,St,Ed;
int Head[205],Next[40005],To[40005],Val[40005],En;
int Dis[205],Pre[205],Cur[205],Cnt[205];
char Suki[55][55];

inline void Add(int x,int y,int z)
{
    Next[++En]=Head[x],To[Head[x]=En]=y,Val[En]=z;
    Next[++En]=Head[y],To[Head[y]=En]=x,Val[En]=0;
}

int Augment()
{
    int Res=0x3f3f3f3f;
    for(int x=Ed;x!=St;x=To[Pre[x]^1])Res=Min(Res,Val[Pre[x]]);
    for(int x=Ed,i;x!=St;x=To[i^1])Val[i=Pre[x]]-=Res,Val[i^1]+=Res;
    return Res;
}

int ISAP()
{
    memset(Dis,0,sizeof Dis);
    memcpy(Cur,Head,sizeof Cur);
    memset(Cnt,0,sizeof Cnt);
    Cnt[0]=Ed;
    int Flow=0,x=St;
    while(Dis[St]<Ed)
    {
        if(x==Ed)Flow+=Augment(),x=St;
        bool Flag=false;
        for(int i=Cur[x],y;i;i=Next[i])
            if(Val[i]&&Dis[y=To[i]]+1==Dis[x])
                Flag=true,Cur[x]=Pre[y]=i,x=y,i=0;
        if(Flag)continue;
        int Wd=Ed-1;
        for(int i=Head[x];i;i=Next[i])
            if(Val[i])Wd=Min(Wd,Dis[To[i]]);
        if(!--Cnt[Dis[x]])break;
        ++Cnt[Dis[x]=Wd+1],Cur[x]=Head[x];
        if(x!=St)x=To[Pre[x]^1];
    }
    return Flow;
}

void Build(int Tar)
{
    memset(Head,0,sizeof Head),En=1;
    St=n<<2|1,Ed=St+1;//源点汇点
    for(int i=1;i<=n;++i)
    {//[1,n]男生喜欢,[n+1,n*2]男生不喜欢
        Add(St,i,Tar),Add(i,n+i,k);
        Add((n<<1)+i,Ed,Tar),Add(n*3+i,(n<<1)+i,k);
    }//[n*2+1,n*3]女生喜欢,[n*3+1,n*4]女生不喜欢
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j)
            if(Suki[i][j]=='Y')
                Add(i,(n<<1)+j,1);
            else
                Add(n+i,n*3+j,1);
}

int main()
{
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;++i)scanf("%s",Suki[i]+1);
    int l=0,r=n+1;
    while(l<r)
    {
        int Mid=(l+r)>>1;
        Build(Mid);
        if(ISAP()==n*Mid)l=Mid+1;
        else r=Mid;
    }
    printf("%d\n",l-1);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/LanrTabe/p/10200438.html
今日推荐