P3153 [CQOI2009]跳舞 二分网络流
Posted bxd123
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P3153 [CQOI2009]跳舞 二分网络流相关的知识,希望对你有一定的参考价值。
题目描述
一次舞会有n个男孩和n个女孩。每首曲子开始时,所有男孩和女孩恰好配成n对跳交谊舞。每个男孩都不会和同一个女孩跳两首(或更多)舞曲。有一些男孩女孩相互喜欢,而其他相互不喜欢(不会”单向喜欢“)。每个男孩最多只愿意和k个不喜欢的女孩跳舞,而每个女孩也最多只愿意和k个不喜欢的男孩跳舞。给出每对男孩女孩是否相互喜欢的信息,舞会最多能有几首舞曲?
输入格式
第一行包含两个整数n和k。以下n行每行包含n个字符,其中第i行第j个字符为‘Y‘当且仅当男孩i和女孩j相互喜欢。
输出格式
仅一个数,即舞曲数目的最大值。
输入输出样例
3 0 YYY YYY YYY
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;
以上是关于P3153 [CQOI2009]跳舞 二分网络流的主要内容,如果未能解决你的问题,请参考以下文章
luogu P3153 [CQOI2009]跳舞 |网络流最大流
luogu P3153 [CQOI2009]跳舞 |网络流最大流
AC日记——[CQOI2009]DANCE跳舞 洛谷 P3153