bzoj1305 [CQOI2009]dance跳舞 最大流 二分

2 篇文章 0 订阅
1 篇文章 0 订阅

bzoj1305 [CQOI2009]dance跳舞
题意:一次舞会有n个男孩和n个女孩。每首曲子开始时,所有男孩和女孩恰好配成n对跳交谊舞。每个男孩都不会和同一个女孩跳两首(或更多)舞曲。 有一些男孩女孩相互喜欢,而其他相互不喜欢(不会“单向喜欢”)。每个男孩最多只愿意和k个不喜欢的女孩跳舞,而每个女孩也最多只愿意和k个不喜欢的男孩跳舞。 给出每对男孩女孩是否相互喜欢的信息,舞会最多能有几首舞曲?n<=50
题解:拆点= =
二分答案(枚举也可以orz)
然后就变成判定性问题啦
然后我们看到这是一个二分图
左边代表男孩 右边代表女孩
我们可以愉快的拆点
每个节点拆成3个点 我们依次叫它node1,2,3
二分图左边的每个节点的前两个分点(node1,2)连的边代表最多跳的舞曲数(二分出来的x)
后两个分点(node2,3)连的边代表最多和不喜欢的人跳舞次数(k)
二分图右边的点反过来
然后总源点和左边的所有node1连inf
又边的所有node3和总汇点连inf
左边node3和不喜欢的人的node1连1
左边node2和喜欢的人的node2连1
然后跑最大流 flow==x*n 代表是可行解

#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
using namespace std;
bool bfs();int dfs(int,int);int dinic();
queue<int>Q;
const int inf=1<<30,Maxn=400,Maxm=400*400;
struct E{int to,cap,flow,nxt;}edge[Maxm*2];
int tot=2,s,t,n,k;
int idx[Maxn],a[Maxm],cur[Maxn],dis[Maxn];
char map[Maxn][Maxn];bool vis[Maxn];
void addedge(int from,int to,int c){
    edge[tot].to=to;edge[tot].cap=c;edge[tot].flow=0;edge[tot].nxt=idx[from];idx[from]=tot++;
}
void add(int from,int to,int cap){addedge(from,to,cap);addedge(to,from,0);}
void init(){
    scanf("%d%d\n",&n,&k);
    for(int i=1;i<=n;i++)
        scanf("%s\n",map[i]+1);
}
int node1(char c,int i){return c=='l'?i:3*n+i;}
int node2(char c,int i){return c=='l'?i+n:4*n+i;}
int node3(char c,int i){return c=='l'?i+2*n:5*n+i;}
void build(int x){
    memset(edge,0,sizeof(E));
    memset(idx,0,sizeof(idx));tot=2;
    s=n*6+1,t=n*6+2;
    for(int i=1;i<=n;i++)
        add(s,node1('l',i),inf),
        add(node3('r',i),t,inf);
    for(int i=1;i<=n;i++){
        add(node1('l',i),node2('l',i),x);
        add(node2('l',i),node3('l',i),k);
        add(node2('r',i),node3('r',i),x);
        add(node1('r',i),node2('r',i),k);
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++){
            if(map[i][j]=='Y') add(node2('l',i),node2('r',j),1);
            else add(node3('l',i),node1('r',j),1);
        }
    //printf("\n");
}
bool judge(int x){return dinic()>=x*n?1:0;}
int main(){
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    init();int l=0,r=n+1,i;
    /*for(i=1;i<=n;i++){
        build(i);
        if(!judge(i)) break;
    }
    printf("%d\n",i-1);*/
    while(l<r){
        int mid=(l+r)/2;
        //printf("%d\n",mid);
        build(mid);
        if(judge(mid)) l=mid+1;
        else r=mid;
    }
    printf("%d\n",l-1);
    return 0;
}
bool bfs(){
    memset(vis,0,sizeof(vis));
    memset(dis,0,sizeof(dis));
    Q.push(s);vis[s]=1;
    while(!Q.empty()){
        int x=Q.front();Q.pop();
        for(int i=idx[x];i;i=edge[i].nxt){
            E e=edge[i];
            if(!vis[e.to] && e.cap>e.flow) 
                vis[e.to]=1,Q.push(e.to),dis[e.to]=dis[x]+1;
        }
    }
    //for(int i=0;i<=T;i++) printf("%d ",dis[i]);
    return vis[t];
}
int dfs(int x,int a){
    int flow=0,f;
    if(x==t || a==0) return a;
    for(int& i=cur[x];i;i=edge[i].nxt){
        E e=edge[i];
        if(dis[x]+1==dis[e.to]){
            f=dfs(e.to,min(a,e.cap-e.flow));
            if(f>0){
                edge[i].flow+=f;
                edge[i^1].flow-=f;
                flow+=f;a-=f;if(!a) break;
            }
        }
    }
    return flow;
}
int dinic(){
    int flow=0;
    while(bfs()){
        for(int i=1;i<=t;i++) cur[i]=idx[i];
        flow+=dfs(s,inf);
    }
    return flow;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值