BZOJ 1305 [CQOI2009]dance跳舞

1305: [CQOI2009]dance跳舞

Time Limit: 5 Sec   Memory Limit: 162 MB
Submit: 2531   Solved: 1044
[ Submit][ Status][ Discuss]

Description

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

Input

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

Output

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

Sample Input

3 0
YYY
YYY
YYY

Sample Output

3

HINT

N<=50 K<=30


【题目分析】

    首先看到题目之后,显然是一种二分图,但是限制又显得十分麻烦,那么就考虑用网络流来做。首先建立一个源点汇点,在考虑每个人都必须跳够x首舞曲的情况下,显然是需要最优转判定的(要不然不能用最大流来显示舞曲的数目)。那么把每一个人拆成3个点(他的本身、表示他喜欢的点、表示他不喜欢的点),然后从原点向他们的自己连一条x的边,然后再将它们本身向表示他喜欢的点连一条正无穷的边,向不喜欢的点连一条k的边,女生也是同理。然后在男女生之间,喜欢的点向喜欢的,不喜欢的点向不喜欢的点连一条1的边。然后跑一边最大流就可以得到结果了。


【代码】

#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <queue>
using namespace std;
#define F(i,j,k) for (int i=j;i<=k;++i)
#define inf 1000000
#define M(a) memset(a,-1,sizeof a)
#define M0(a) memset(a,0,sizeof a)
#define maxn 100000
int match[51][51];
int h[maxn],fr[maxn],go[maxn],ne[maxn],f[maxn],map[maxn];
int n,k,S,T,en=0;
inline void add(int a,int b,int r)
{
	f[en]=r;fr[en]=a;go[en]=b;ne[en]=h[a];h[a]=en++;
	f[en]=0;fr[en]=b;go[en]=a;ne[en]=h[b];h[b]=en++;
}
inline bool tell()
{
	M(map);
	queue <int> q;
	q.push(S);
	map[S]=0;
	while (!q.empty())
	{
		int x=q.front(); q.pop();
		for (int i=h[x];i!=-1;i=ne[i])
		{
			if (map[go[i]]==-1&&f[i]){
				map[go[i]]=map[x]+1;
				q.push(go[i]);
				
			}
		}
	}
	if (map[T]!=-1) return true;
	else return false;
}
inline int zeng(int kk,int now)
{
	if (kk==T) return now;
	int r=0;
	for (int i=h[kk];i!=-1&&now>r;i=ne[i])
	{
		if (map[kk]+1==map[go[i]]&&f[i]!=0)
		{
		   int addi=zeng(go[i],min(now-r,f[i]));
		   f[i]-=addi; f[i^1]+=addi; r+=addi;
		}
	}
	if (!r) map[kk]=-1;
	return r;
}
inline int dinic()
{
	int r=0,now=0;
	while (tell()) while (now=zeng(S,inf)) r+=now;
	return r;
}
inline bool tr(int run)
{
	en=0;
	M(h);M(fr);M(go);M(ne);M0(f);
	F(i,1,n) add(0,i*3-2,run),add(i*3-2,i*3-1,inf),add(i*3-2,i*3,k);
	F(i,1,n) add(3*n+i*3-2,T,run),add(3*n+i*3-1,3*n+i*3-2,inf),add(3*n+i*3,3*n+i*3-2,k);
	F(i,1,n) F(j,1,n)
	{
		if (match[i][j]!=0) add(i*3-1,3*n+j*3-1,1);
		else add(i*3,3*n+j*3,1);
	}
	if (dinic()>=run*n) return true;
	else return false;
}
int main()
{
	scanf("%d%d",&n,&k);
	F(i,1,n)F(j,1,n){char ch;cin>>ch;if(ch=='Y')match[i][j]=1;}
	S=0;T=6*n+1;
	int l=0,r=n;
	while (l<r)
	{
		int mid=(l+r)/2+1;
		if (tr(mid)) l=mid;
		else r=mid-1;
	}
	printf("%d\n",l);
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值