P3153 [CQOI2009]跳舞 二分网络流

Posted bxd123

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P3153 [CQOI2009]跳舞 二分网络流相关的知识,希望对你有一定的参考价值。

  

题目描述

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

输入格式

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

输出格式

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

输入输出样例

输入 #1
3 0
YYY
YYY
YYY
输出 #1
3


1.都要跳舞

既然不能干看着,那么一首舞曲中,每个人都得跳,换言之,若是本题答案为ans,那么最终每人都能跳ans只舞,

所以我们二分答案,在某个模型下跑最大流,最后检查答案,若满足条件:(跳舞的总数就是ans * N(人数))我们就往上搜,否则就往下搜,不断缩小二分范围,找到答案 (其实貌似数据范围可以直接枚举)

2.不会和同一人跳舞

匹配的基本知识,男女连边容量为1即可,不再赘述

3.能和k个不喜欢的人跳舞

我们要求能跳舞的场数最多,自然就想k尽可能多一点,虽然k是不能改变的,但是依据贪心的思想,我们能不用k就尽量不用k,换言之,如果和舞伴互相喜欢,就不用消耗k的次数了。

为了达到喜欢的人互相跳舞不消耗k,我们需要分裂点:将每个男/女分裂为喜欢和不喜欢两个部分,然后连边如下图

技术图片

a为二分出来的答案 源点连男容量为a保证了一个人可以跳a场舞,来进行最大流以及答案验证 (深色为喜欢,浅一点为不喜欢)

若两人相互喜欢,则有:

技术图片

这样直接连喜欢,s到t的路上没有占用k,即k的次数无消耗;容量为1保证了只和同一人跳一次舞


若两人不互相喜欢,则有

技术图片

不喜欢的人跳舞不愿意,需要消耗k,路径被夹在k之间,最大流从之间通过消耗k,达到目的;容量为1同样保证了只和同一人跳一次舞


技术图片
#include<bits/stdc++.h>
using namespace std;
//input by bxd
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define repp(i,a,b) for(int i=(a);i>=(b);--i)
#define RI(n) scanf("%d",&(n))
#define RII(n,m) scanf("%d%d",&n,&m)
#define RIII(n,m,k) scanf("%d%d%d",&n,&m,&k)
#define RS(s) scanf("%s",s);
#define ll long long
#define pb push_back
#define REP(i,N)  for(int i=0;i<(N);i++)
#define CLR(A,v)  memset(A,v,sizeof A)
//////////////////////////////////
#define inf 0x3f3f3f3f
const int N=4e5+44;
const int M=4e6+54;
int d[N];
struct edge 
    int to, next, w;
 e[M << 1];
int head[N],cur[N],cnt = 1;
void add(int x, int y, int z) 
    e[++cnt] = (edge)y, head[x], z;
    head[x] = cnt;
    e[++cnt] = (edge)x, head[y], 0;
    head[y] = cnt;

void init()

    cnt=1;CLR(head,0);CLR(cur,0);

void ins(int x,int y,int a,int b)

    add(x,y,b-a);
    d[x]-=a;
    d[y]+=a;


int level[N];
bool bfs(int s, int t) 
    memset(level, 0, sizeof level);
    queue<int> q;
    level[s] = 1;
    q.push(s);
    while (!q.empty()) 
        int pos = q.front();q.pop();
        for (int i = head[pos]; i; i = e[i].next) 
            int nx = e[i].to;
            if (!e[i].w || level[nx]) continue;
            level[nx] = level[pos] + 1;
            q.push(nx);
        
    
    return level[t];

int dfs(int s, int t, int flow) 
    if(s==t||flow==0)return flow;

    int f,ret = 0;
    for (int &i = cur[s],v; i; i = e[i].next) 
         v = e[i].to;
        if (level[v] == level[s] + 1 && (f=dfs(v,t,min(flow,e[i].w)))>0) 
            e[i].w -= f;
            e[i ^ 1].w += f;
            flow -= f;
            ret += f;
            if(!flow)break;
        
    
    return ret;

int dinic(int s, int t) 
    int ret = 0;
    while (bfs(s, t)) memcpy(cur,head,sizeof cur),ret += dfs(s, t, inf);
    return ret;

int n,m,s,t,a,b,c,sum,S,T,k;
char mp[100][100];

void build(int a)

    init();
    rep(i,1,n)
    
        add(s,i,a);
        add(i,i+n,k);
        add(i+2*n,i+3*n,k);
        add(i+3*n,t,a);
    
    rep(i,1,n)
    rep(j,1,n)
    
        if(mp[i][j]==Y)add(i,j+3*n,1);
        else add(i+n,j+2*n,1);
    


bool check(int x)

    build(x);
    return dinic(s,t)==n*x;


int main()

    scanf("%d%d",&n,&k);s=10*n+1,t=s+1;
    rep(i,1,n)scanf("%s",mp[i]+1);


    int L=0,R=n,ans=0;
    while(L<=R)
    
        int mid=(L+R)>>1;
        if(check(mid))L=mid+1,ans=mid;
        else R=mid-1;
    
    cout<<ans;
    return 0;
View Code

 





以上是关于P3153 [CQOI2009]跳舞 二分网络流的主要内容,如果未能解决你的问题,请参考以下文章

luogu P3153 [CQOI2009]跳舞 |网络流最大流

luogu P3153 [CQOI2009]跳舞 |网络流最大流

AC日记——[CQOI2009]DANCE跳舞 洛谷 P3153

bzoj1305: [CQOI2009]dance跳舞(二分答案+网络流)

P3153 [CQOI2009]跳舞

BZOJ 1305 [CQOI2009]dance跳舞(二分+网络流)