1. 程式人生 > >1305. [CQOI2009]跳舞【最大流+二分】

1305. [CQOI2009]跳舞【最大流+二分】

else edge fine string 男女 nbsp CP ios clu

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首曲子,則x-1首肯定也是可以的
建圖?
將每個男生和女生拆成兩個點:Yes和No

超級源點連接男生的Yes,超級匯點連接女生的Yes
容量設置為二分的x。若跑出來最大流是x*n,就說明滿流了
滿流即為可以滿足x首曲子

對於互相喜歡的,我們在男女的兩個Yes點間連邊
不喜歡的,我們就在男女的兩個No點間連邊
那麽如何限制k呢?
每個男Yes連自己的No,容量為k
每個女No連自己Yes,容量為k
這樣就可以保證k了

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#define MAXM (100000+10)
#define MAXN (10000+10)
using namespace std;
struct node
{
    int Flow;
    int next;
    int to;
}edge[MAXM*2];
int Depth[MAXN],q[MAXN];
int head[MAXN],num_edge;
int n,m,k,s,e,d,INF;
int a[MAXN][MAXN];
char ch[1010];

void add(int u,int v,int l)
{
    edge[++num_edge].to=v;
    edge[num_edge].Flow=l;
    edge[num_edge].next=head[u];
    head[u]=num_edge;
}

bool Bfs(int s,int e)
{
    int Head=0,Tail=1;
    memset(Depth,0,sizeof(Depth));
    Depth[s]=1;
    q[1]=s;
    while (Head<Tail)
    {
        ++Head;
        int x=q[Head];
        for (int i=head[x];i!=0;i=edge[i].next)
            if (!Depth[edge[i].to] && edge[i].Flow>0)
            {
                Depth[edge[i].to]=Depth[x]+1;
                q[++Tail]=edge[i].to;
            }
    }
    if (Depth[e]>0) return true;
    return false;
}

int Dfs(int x,int low)
{
    int Min,f=0;
    if (x==e || low==0)
        return low;
    for (int i=head[x];i!=0;i=edge[i].next)
        if (edge[i].Flow>0 && Depth[edge[i].to]==Depth[x]+1 && (Min=Dfs(edge[i].to , min(low,edge[i].Flow) )))
        {
            edge[i].Flow-=Min;
            edge[((i-1)^1)+1].Flow+=Min;
            f+=Min;
            low-=Min;
        }
    return f;
}

int Dinic(int s,int e)
{
    int Ans=0;
    while (Bfs(s,e))
            Ans+=Dfs(s,0x7fffffff);
    return Ans;
}

void Addline(int x)
{
	memset(head,0,sizeof(head));
	memset(edge,0,sizeof(edge));
	num_edge=0;
	for (int i=1;i<=n;++i)
	{
		add(0,i+520,x);
		add(i+520,0,0);//超級源點 
		
		add(i+n+520,999,x);//超級匯點 
		add(999,i+n+520,0);
		
		add(i+520,i+250,k);
		add(i+250,i+520,0);
		
		add(i+n+250,i+n+520,k);
		add(i+n+520,i+n+250,0); 
		
	}
	for (int i=1;i<=n;++i)
	{
		for (int j=1;j<=n;++j)
			if (a[i][j]==1)
			{
				add(i+520,j+n+520,1);
				add(j+n+520,i+520,0);
			}
			else
			{
				add(i+250,j+n+250,1);
				add(j+n+250,i+250,0);
			}
	}
}

int main()
{
	s=0,e=999;
	memset(&INF,0x7f,sizeof(INF));
	scanf("%d%d",&n,&k);
	for (int i=1;i<=n;++i)
	{
		scanf("%s",ch);
		for (int j=1;j<=n;++j)
			a[i][j]=(ch[j-1]==‘Y‘);
	}
	int l=0,r=n*n;
	while (l<r)
	{
		int mid=(l+r+1)/2;
		Addline(mid);
		int Max=Dinic(0,999);
		if (Max==mid*n)
			l=mid;
		else
			r=mid-1;
	}
	cout<<l<<endl;
}

1305. [CQOI2009]跳舞【最大流+二分】