BZOJ1305 || 洛谷P3153 [CQOI2009]跳舞【最大流】

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/niiick/article/details/82898859

Time Limit: 5 Sec
Memory Limit: 162 MB

Description

一次舞会有n个男孩和n个女孩。每首曲子开始时,所有男孩和女孩恰好配成n对跳交谊舞。每个男孩都不会和同一个女孩跳两首(或更多)舞曲。有一些男孩女孩相互喜欢,而其他相互不喜欢(不会“单向喜欢”)。每个男孩最多只愿意和k个不喜欢的女孩跳舞,而每个女孩也最多只愿意和k个不喜欢的男孩跳舞。给出每对男孩女孩是否相互喜欢的信息,舞会最多能有几首舞曲?

Input

第一行包含两个整数n和k。以下n行每行包含n个字符,其中第i行第j个字符为’Y’当且仅当男孩i和女孩j相互喜欢。

Output

仅一个数,即舞曲数目的最大值。


题目分析

直接计算似乎不太方便,所以我们尝试二分一个最大值再判断是否可行
把每个点拆成喜欢与不喜欢两个点
即男生喜欢/不喜欢,女生喜欢/不喜欢 共4n个点

设当前二分值为mid
那么由超源向每个男生喜欢连边,容量为mid
每个女生不喜欢向超汇连边,容量为mid

每个男生由自己的喜欢向自己的不喜欢连边,容量为K
每个女生也同理

对于每对关系
男生喜欢向女生喜欢男生不喜欢向女生不喜欢 连边,容量为1

若最大流为mid*n,则证明此二分值可行


#include<iostream>
#include<cstdio>
#include<queue>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
 
int read()
{
    int x=0,f=1;
    char ss=getchar();
    while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
    while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
    return f*x;
} 
 
const int inf=1e9;
int n,k;
int s,t;
char cp[100][100];
struct node{int v,f,nxt;}E[1000010];
int head[100010],tot=1;
int lev[100010];
int ans,maxf;
 
void add(int u,int v,int f)
{
    E[++tot].nxt=head[u];
    E[tot].v=v;
    E[tot].f=f;
    head[u]=tot; 
}
 
bool bfs()
{
    queue<int> q; q.push(s);
    memset(lev,-1,sizeof(lev)); lev[s]=0;
     
    while(!q.empty())
    {
        int u=q.front(); q.pop();
        for(int i=head[u];i>0;i=E[i].nxt)
        {
            int v=E[i].v;
            if(lev[v]==-1&&E[i].f)
            {
                lev[v]=lev[u]+1;
                if(v==t) return true;
                q.push(v);
            }
        }
    }
    return false;
}
 
int dfs(int u,int cap)
{
    if(u==t) return cap;
    int flow=cap;
    for(int i=head[u];i>0;i=E[i].nxt)
    {
        int v=E[i].v;
        if(lev[v]==lev[u]+1&&E[i].f&&flow)
        {
            int f=dfs(v,min(flow,E[i].f));
            flow-=f;
            E[i].f-=f; E[i^1].f+=f; 
        } 
    }
    return cap-flow;
}
 
void build(int mid)
{
    //i男生喜欢,i+n男生不喜欢,i+2*n女生喜欢,i+3*n女生不喜欢
    for(int i=1;i<=n;++i)
    {
        int num=i+n*3;
        add(s,i,mid);add(i,s,0);//超源->男生喜欢
        add(num,t,mid);add(t,num,0);//女生不喜欢->超汇
             
        add(i,i+n,k);add(i+n,i,0);//男生/女生喜欢->男生/女生不喜欢
        add(num-n,num,k);add(num,num-n,0);
             
        for(int j=0;j<n;j++)
        {
            int gy=j+1+n*3,gd=j+1+(n<<1);
            if(cp[i][j]=='Y')add(i,gy,1),add(gy,i,0);//男生喜欢->女生喜欢
            else add(i+n,gd,1),add(gd,i+n,0);//男生不喜欢->女生不喜欢
        }
    }
}
 
void init()
{
    memset(head,-1,sizeof(head));
    tot=1; maxf=0;
}
 
int main()
{
    n=read();k=read(); t=n<<2|1;
    for(int i=1;i<=n;++i)
    scanf("%s",&cp[i]);
     
    int ll=0,rr=n;
    while(ll<rr)
    {
        int mid=ll+rr>>1;
        build(mid);
         
        while(bfs())
        maxf+=dfs(s,inf);
         
        if(maxf==mid*n)ans=mid,ll=mid+1;
        else rr=mid;
        init();
    }
    build(rr);
    while(bfs())
    maxf+=dfs(s,inf);
     
    if(maxf==rr*n) cout<<rr;
    else cout<<ans;
    return 0;
}


猜你喜欢

转载自blog.csdn.net/niiick/article/details/82898859
今日推荐