题解 - P3153 [CQOI2009]跳舞
题目描述
一次舞会有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
思路:
我们一个一个分析:
- 都要跳舞
既然不能干看着,那么一首舞曲中,每个人都得跳,换言之,若是本题答案为ans,那么最终每人都能跳ans只舞,
所以我们二分答案,在某个模型下跑最大流,最后检查答案,若满足条件:(跳舞的总数就是ans * N(人数))我们就往上搜,否则就往下搜,不断缩小二分范围,找到答案 (其实貌似数据范围可以直接枚举)
- 不会和同一人跳舞
匹配的基本知识,男女连边容量为1即可,不再赘述
- 能和k个不喜欢的人跳舞
我们要求能跳舞的场数最多,自然就想k尽可能多一点,虽然k是不能改变的,但是依据贪心的思想,我们能不用k就尽量不用k,换言之,如果和舞伴互相喜欢,就不用消耗k的次数了。
说明一下,入边都是k,出边也是k,如何证明一定存在合法方案,hull定理:二分图中,任意k的点有不同的任意k的连边,可以构造出一个完美匹配。于是每个点出k条,任意x个点,有kx条边,右侧每个点最多接受k条,所以至少有k个不同的点。我们取这一种完美匹配。转化成出入边都有(k-1)条,同理构造即可。
于是二分加网络流判断就好了。(记得初始化)
code
#include<bits/stdc++.h>
using namespace std;
int a[100][100],head[500005],nex[500005],go[500005],w[500005],k,n,d,l,r,mid,S,T,bi=1,lev[500005],cnt[500005];
void add(int x,int y,int z)
{
nex[++bi]=head[x];
head[x]=bi;
go[bi]=y;
w[bi]=z;
}
int dfs(int u,int flow)
{
if(u==T)
return flow;
int res=0,sum=0;
for(int i=head[u];i;i=nex[i])
{
int v=go[i];
if(w[i]&&lev[u]==lev[v]+1)
{
sum=dfs(v,min(flow-res,w[i]));
if(sum)
{
w[i]-=sum;
w[i^1]+=sum;
res+=sum;
}
}
if(res==flow) return flow;
}
if(--cnt[lev[u]]==0)
lev[S]=T+1;
++cnt[++lev[u]];
return res;
}
void bfs()
{
memset(lev,-1,sizeof(lev));
memset(cnt,0,sizeof(cnt));
cnt[0]=1;
queue <int> q;
q.push(T);
lev[T]=0;
while(!q.empty())
{
int u=q.front();
q.pop();
for(int i=head[u];i;i=nex[i])
{
int v=go[i];
if(lev[v]==-1)
{
cnt[lev[v]=lev[u]+1]++;
q.push(v);
}
}
}
}
int pd(int x)
{
bi=1;
memset(head,0,sizeof(head));//注意清零
for(int i=1;i<=n;i++)
{
add(i,i+n,k);//不喜欢限制
add(i+n,i,0);
add(i+3*n,i+2*n,k);
add(i+2*n,i+n*3,0);
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(a[i][j]==1)
{
add(i,j+n*2,1);//一对只能匹配一次,连1的边
add(j+n*2,i,0);
}
else{
add(n+i,j+3*n,1);
add(j+3*n,i+n,0);
}
}
}
for(int i=1;i<=n;i++)
{
add(0,i,x);
add(i,0,0);
add(i+2*n,T,x);//处理起点终点
add(T,i+n*2,0);
}
long long ans=0;
bfs();
while(lev[S]<T)
{
ans+=dfs(S,1e9);
}
//cout<<ans<<endl;
if(ans==x*n)
return 1;
return 0;
}
signed main()
{
cin>>n>>k;
char ss[100][100];
for(int i=1;i<=n;i++)
{
scanf("%s",ss[i]+1);
for(int j=1;j<=n;j++)
{
if(ss[i][j]=='Y')
{
a[i][j]=1;
}
}
}
// cout<<endl<<endl<<endl;
l=1,r=n;
S=0;
T=4*n+1;
while(l<=r)
{
int mid=l+r>>1;
if(pd(mid))
l=mid+1;
else{
r=mid-1;
}
}
cout<<l-1;
return 0;
}