[CQOI2009]DANCE跳舞

题目描述

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

输入输出格式

输入格式:

 

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

 

输出格式:

 

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

 

输入输出样例

输入样例#1:
3 0
YYY
YYY
YYY
输出样例#1:
3

说明

N<=50 K<=30

 

题解:

很好想的最大流:

对于相互喜欢的boy and girl 我们就直接连边 容量为1

对于不喜欢的,我们要限制和不喜欢的人跳舞的人数不超过k,于是拆点,连 (i,i+n,k)

boy 和 girl 都要拆点,如果不喜欢,就将boy的辅助点和girl的辅助点相连 容量为1,就可以保证双方都<=k

然后就是二分答案,S出边和T入边权值改为mid即可

 

 1 #include <algorithm>
 2 #include <iostream>
 3 #include <cstdlib>
 4 #include <cstring>
 5 #include <cstdio>
 6 #include <cmath>
 7 using namespace std;
 8 const int SIZE=55,N=205,M=80005,INF=2e9;
 9 int n,k;int map[SIZE][SIZE];char s[SIZE];
10 int num=1,head[N];
11 struct Lin{
12     int next,to,dis;
13 }a[M];
14 void init(int x,int y,int z){
15     a[++num].next=head[x];a[num].to=y;a[num].dis=z;head[x]=num;
16 }
17 void addedge(int x,int y,int z){
18     init(x,y,z);init(y,x,0);
19 }
20 int S=0,T,q[N],dep[N];
21 bool bfs(){
22     memset(dep,0,sizeof(dep));
23     int t=0,sum=1,x,u;
24     q[1]=S;dep[S]=1;
25     while(t!=sum){
26         x=q[++t];
27         for(int i=head[x];i;i=a[i].next){
28             u=a[i].to;
29             if(dep[u] || a[i].dis<=0)continue;
30             dep[u]=dep[x]+1;q[++sum]=u;
31         }
32     }
33     return dep[T];
34 }
35 int dfs(int x,int flow){
36     if(x==T || !flow)return flow;
37     int u,tot=0,tmp;
38     for(int i=head[x];i;i=a[i].next){
39         u=a[i].to;
40         if(dep[u]!=dep[x]+1 || a[i].dis<=0)continue;
41         tmp=dfs(u,min(flow,a[i].dis));
42         a[i].dis-=tmp;a[i^1].dis+=tmp;
43         tot+=tmp;flow-=tmp;
44         if(!flow)break;
45     }
46     if(!tot)dep[x]=0;
47     return tot;
48 }
49 int maxflow(){
50     int tot=0,tmp;
51     while(bfs()){
52         tmp=dfs(S,INF);
53         while(tmp)tot+=tmp,tmp=dfs(S,INF);
54     }
55     return tot;
56 }
57 void Clear(){
58    num=1;
59     memset(head,0,sizeof(head));
60 }
61 bool check(int lim){
62     Clear();
63     for(int i=1;i<=n;i++){
64         addedge(S,i,lim);addedge(i+n+n,T,lim);
65         addedge(i,i+n,k);addedge(i+n+n+n,i+n+n,k);
66     }
67     for(int i=1;i<=n;i++)
68         for(int j=1;j<=n;j++){
69             if(map[i][j])addedge(i,j+n+n,1);
70             else addedge(i+n,j+n+n+n,1);
71         }
72     int ret=maxflow();
73     return ret==lim*n;
74 }
75 void work(){
76     scanf("%d%d",&n,&k);T=4*n+1;
77     for(int i=1;i<=n;i++){
78         scanf("%s",s);
79         for(int j=1;j<=n;j++)if(s[j-1]=='Y')map[i][j]=true;
80     }
81     int l=1,r=n,mid,ans=0;
82     while(l<=r){
83         mid=(l+r)>>1;
84         if(check(mid))ans=mid,l=mid+1;
85         else r=mid-1;
86     }
87     printf("%d\n",ans);
88 }
89 int main()
90 {
91     work();
92     return 0;
93 }

 

转载于:https://www.cnblogs.com/Yuzao/p/7214569.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值