bzoj1035(最大流)

1305: [CQOI2009]dance跳舞

Time Limit: 5 Sec   Memory Limit: 162 MB
Submit: 2213   Solved: 919
[ 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


解题思路:

  

先把每个人i拆分成ix和iy两个节点,ix连向喜欢的人,iy连向不喜欢的人,容量为1(比如如果男生i与女生j互相喜欢,则由ix连向jx,如果男生i与女生j互相不喜欢,则由iy连向jy),再将每个男生男生ix连向iy,容量为k;每个女生iy连向ix,容量为k。由源点向每个男生的x节点连上一条边,再由每个女生的x节点向汇点连上一条边,容量均为a。最后从小到大枚举a,计算最大流flow,若发现a*n>flow(不满流),则停止枚举,a-1即为答案。

或者可以二分


#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int n,k;
const int ma=0x7fffffff;
int dis[211];
int e[211][211];
int q[211];
int zan[211][211];


bool bfs()
 {
  memset(dis,-1,sizeof(dis));
  dis[0]=0; int head=0,tail=0; ++tail; q[tail]=0;
  while (head<tail)
  {
  ++head;
  for (int i=1;i<=4*n+1;++i)
  if (e[q[head]][i]>0 && dis[i]==-1)
 {
  dis[i]=dis[q[head]]+1;
  ++tail; q[tail]=i;
   }
  }
  if (dis[4*n+1]==-1) return false;else return true;
 }


int dinic(int o,int flow)
 {
  if (o==4*n+1) return flow;
  int used=0;
  for (int i=1;i<=4*n+1;++i)
  if (dis[i]==dis[o]+1 && e[o][i]>0)
 {
  int ss=dinic(i,min(flow-used,e[o][i]));
if (ss)
{
e[o][i]-=ss; e[i][o]+=ss;
used+=ss;
if (used==flow) return flow;

  }
  if (used==0) dis[o]=-1;
  return used;
 }


int main()
{
scanf("%d %d",&n,&k);
for (int i=n+1;i<=2*n;++i) e[i-n][i]=k;
for (int i=1;i<=n;++i)
{
     getchar();
for (int j=1;j<=n;++j)
 {
  char c;
  c=getchar();
  if (c=='Y')
  {
  e[i][j+2*n]=1;
  }else
   {
    e[i+n][j+3*n]=1;
   }
 }
}
for (int i=3*n+1;i<=4*n;++i) e[i][i-n]=k;
for (int i=1;i<=4*n+1;++i)
for (int j=1;j<=4*n+1;++j)
 zan[i][j]=e[i][j];
for (int y=1;y<=n;++y)
{
for (int i=1;i<=n;++i) e[0][i]=y;
for (int i=2*n+1;i<=3*n;++i) e[i][4*n+1]=y;
int sum=0;
while (bfs())
{
sum+=dinic(0,ma);
}
if (sum<y*n) {cout<<y-1;return 0;}
for (int i=1;i<=4*n+1;++i)
    for (int j=1;j<=4*n+1;++j)
     e[i][j]=zan[i][j];
}
cout<<n;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值