P3153 [CQOI2009]跳舞
Posted five20
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P3153 [CQOI2009]跳舞相关的知识,希望对你有一定的参考价值。
题目描述
一次舞会有n个男孩和n个女孩。每首曲子开始时,所有男孩和女孩恰好配成n对跳交谊舞。每个男孩都不会和同一个女孩跳两首(或更多)舞曲。有一些男孩女孩相互喜欢,而其他相互不喜欢(不会”单向喜欢“)。每个男孩最多只愿意和k个不喜欢的女孩跳舞,而每个女孩也最多只愿意和k个不喜欢的男孩跳舞。给出每对男孩女孩是否相互喜欢的信息,舞会最多能有几首舞曲?
输入输出格式
输入格式:第一行包含两个整数n和k。以下n行每行包含n个字符,其中第i行第j个字符为‘Y‘当且仅当男孩i和女孩j相互喜欢。
输出格式:仅一个数,即舞曲数目的最大值。
输入输出样例
3 0
YYY
YYY
YYY
3
说明
N<=50 K<=30
Solution:
本题太毒,调了几天,终于又填完坑了~
像这种需要配对,而且数据还这么小的题目,一眼就容易想到网络最大流。
那么如果直接去跑最大流的话,显然不可行。
题意中说相同的两个人只能搭配一次,那么最多也就$50$次,很容易想到从大到小枚举天数然后跑最大流判断(我写了下枚举+最大流,事实证明是可以过的),但是,本题有很明显的单调性,即若前$i$天可以完整搭配,则答案一定在$[i,n]$之间,否则就在$[0,i-1]$之间。于是考虑二分答案,然后跑最大流$check$。
再来考虑最大流$check$是否可行。每个男生的点和女生的点相匹配,只有两种情况,要么不互相喜欢使用$1$次限制,要么互相喜欢不需要花费。
因为每人最多和不喜欢的匹配$k$次,于是我们将每个学生都拆成两个点,之间连边为$k$表示限制,假设男生$a$被拆为$a1,a2$($a1$是$a$的全局,$a2$是与$a$不互相喜欢的分点),女生$b$被拆为$b1,b2$(类比男生的含义),每次二分的天数$x$,重新建图:$s\rightarrow a1$连容量为$x$($s$为源点,该边表示每个人应该匹配$x$次),$a1\rightarrow a2$连容量为$k$(表示$a$最多和$k$个不喜欢的女生匹配),$b1,b2$类比男生连法($b2\rightarrow b1\;\;b1\rightarrow t$)。每次若男生$a$和女生$b$不喜欢,连容量为$1$的边$a2\rightarrow b2$,若$a$和$b$互相喜欢,则应直接连容量为$1$的边$a1\rightarrow b1$。
然后每次跑完最大流后,看最大流是否等于$x*n$,便能判断是否成立。(最后需要注意的是二分的边界值:$l=0,r=n$,最少就是$1$次也无法搭配,最多就是$n$人互相搭配一次)
代码:
1 #include<bits/stdc++.h> 2 #define il inline 3 #define For(i,a,b) for(int (i)=(a);(i)<=(b);(i)++) 4 #define Min(a,b) ((a)>(b)?(b):(a)) 5 #define debug printf("%d %s\n",__LINE__,__FUNCTION__) 6 using namespace std; 7 const int N=100005,inf=23333333; 8 int s,t=5200,ans,dis[10005],n,k,to[N],net[N],h[10010],cnt=1,w[N]; 9 bool mp[55][55]; 10 11 il void add(int u,int v,int c){to[++cnt]=v,net[cnt]=h[u],h[u]=cnt,w[cnt]=c;} 12 13 il bool bfs(){ 14 queue<int>q; 15 memset(dis,-1,sizeof(dis)); 16 q.push(s),dis[s]=0; 17 while(!q.empty()){ 18 int u=q.front();q.pop(); 19 for(int i=h[u];i;i=net[i]) 20 if(dis[to[i]]==-1&&w[i]>0)dis[to[i]]=dis[u]+1,q.push(to[i]); 21 } 22 return dis[t]!=-1; 23 } 24 25 il int dfs(int u,int op){ 26 if(u==t)return op; 27 int flow=0,used=0; 28 for(int i=h[u];i;i=net[i]){ 29 int v=to[i]; 30 if(dis[v]==dis[u]+1&&w[i]>0){ 31 used=dfs(v,Min(w[i],op)); 32 if(!used)continue; 33 flow+=used,op-=used; 34 w[i]-=used,w[i^1]+=used; 35 if(!op)break; 36 } 37 } 38 if(!flow)dis[u]=-1; 39 return flow; 40 } 41 42 il bool check(int x){ 43 memset(h,0,sizeof(h)); 44 cnt=1; 45 For(i,1,n){ 46 add(s,i,x),add(i,s,0); 47 add(i,i+n,k),add(i+n,i,0); 48 add(i+n*3,t,x),add(t,i+n*3,0); 49 add(i+n*2,i+n*3,k),add(i+n*3,i+n*2,0); 50 } 51 For(i,1,n) For(j,1,n){ 52 if(mp[i][j])add(i,j+3*n,1),add(j+3*n,i,0); 53 else add(i+n,j+2*n,1),add(j+2*n,i+n,0); 54 } 55 int tot=0; 56 while(bfs())tot+=dfs(s,inf); 57 if(tot==n*x)return 1; 58 return 0; 59 } 60 61 int main(){ 62 ios::sync_with_stdio(0); 63 cin>>n>>k; 64 char p; 65 For(i,1,n) For(j,1,n) { 66 cin>>p; 67 if(p==‘Y‘)mp[i][j]=1; 68 if(n==1&&(p==‘Y‘||k>=1)){cout<<1;return 0;} 69 } 70 int mid,l=0,r=n; 71 while(l<=r){ 72 mid=l+r>>1; 73 if(check(mid))l=mid+1,ans=mid; 74 else r=mid-1; 75 } 76 cout<<ans; 77 return 0; 78 }
以上是关于P3153 [CQOI2009]跳舞的主要内容,如果未能解决你的问题,请参考以下文章
luogu P3153 [CQOI2009]跳舞 |网络流最大流