P3153 [CQOI2009]跳舞

题目描述

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

输入输出格式

输入格式:

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

输出格式:

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


分析

首先看的出这道题是一个匹配问题,自然联想到网络流算法

既然是网络流,那么就一定有网络容量来达到限制的目的,最后对应题意的要求。而通过题意找限制条件,构建模型最后用最大流求解,是最大流类题目的核心解法

现在我们来看看这题有什么限制:

1.所有男孩/女孩都要跳舞,不能在旁边干看着

2.不会和同一人跳舞

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

依据这些条件,我们怎么构建模型呢?

建模

我们一个一个分析:

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同样保证了只和同一人跳一次舞

建模完毕

最后跑最大流,验证是否合法,继续二分即可得到最终答案

注:重复建图记得初始化

AC Code

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
int RD(){
int out = 0,flag = 1;char c = getchar();
while(c < '0' || c >'9'){if(c == '-')flag = -1;c = getchar();}
while(c >= '0' && c <= '9'){out = out * 10 + c - '0';c = getchar();}
return flag * out;
}
const int maxn = 100019,INF = 1e9;
int num,k,nume = 1;
int dance[190][190];
int s,t,maxflow;
int head[maxn << 2];
struct Node{
int v,dis,nxt;
}E[maxn << 3];
void add(int u,int v,int dis){
E[++nume].nxt = head[u];
E[nume].v = v;
E[nume].dis = dis;
head[u] = nume;
}
int d[maxn];
bool bfs(){
queue<int>Q;
memset(d,0,sizeof(d));
d[s] = 1;
Q.push(s);
while(!Q.empty()){
int u = Q.front();Q.pop();
for(int i = head[u];i;i = E[i].nxt){
int v = E[i].v;
if(E[i].dis && !d[v]){
d[v] = d[u] + 1;
if(v == t)return 1;
Q.push(v);
}
}
}
return 0;
}
int Dinic(int u,int flow){
if(u == t)return flow;
int rest = flow,k;
for(int i = head[u];i;i = E[i].nxt){
int v = E[i].v;
if(d[v] == d[u] + 1 && rest && E[i].dis){
k = Dinic(v,min(rest,E[i].dis));
if(!k)d[v] = 0;
E[i].dis -= k;
E[i ^ 1].dis += k;
rest -= k;
}
}
return flow - rest;
}
void build(int a){
memset(head,0,sizeof(head));
nume = 1;//初始化
for(int i = 1;i <= num;i++){
add(s,i,a);
add(i,s,0);//连汇点到男生喜欢
add(i,i + num,k);
add(i + num,i,0);//连男喜欢到不喜欢
add(i + 2 * num,i + 3 * num,k);
add(i + 3 * num,i + 2 * num,0);//连女不喜欢到喜欢
add(i + 3 * num,t,a);
add(t,i + 3 * num,0);//女喜欢到源点
}
for(int i = 1;i <= num;i++){
for(int j = 1;j <= num;j++){
if(dance[i][j]){
add(i,j + 3 * num,1);
add(j + 3 * num,i,0);
}
else{
add(i + num,j + 2 * num,1);
add(j + 2 * num,i + num,0);
}
}
}
}
bool check(int mid){
build(mid);
maxflow = 0;
int flow = 0;
while(bfs())while(flow = Dinic(s,INF))maxflow += flow;
if(maxflow == mid * num)return 1;
return 0;
}
int search(int l,int r){
int ans;
while(l <= r){
int mid = l + r >> 1;
if(check(mid))l = mid + 1,ans = mid;
else r = mid - 1;
}
return ans;
}
int main(){
num = RD();k = RD();
char temp;
for(int i = 1;i <= num;i++){
for(int j = 1;j <= num;j++){
cin>>temp;
if(temp == 'Y')dance[i][j] = 1;
}
}
s = num * 4 + 1;t = s + 1;
printf("%d\n",search(0,num + k));
return 0;
}

题解 P3153 【[CQOI2009]跳舞】的更多相关文章

  1. [洛谷P3153] [CQOI2009]跳舞

    题目大意:有n个女生,n个男生,每次一男一女跳舞.同一队只会跳一次.每个男孩最多只愿意和k个不喜欢的女孩跳舞,女孩同理.问舞会最多能有几首舞曲? 题解:二分跳了多少次舞,每次重建图,建超级原点和汇点, ...

  2. P3153 [CQOI2009]跳舞

    题目描述 一次舞会有n个男孩和n个女孩.每首曲子开始时,所有男孩和女孩恰好配成n对跳交谊舞.每个男孩都不会和同一个女孩跳两首(或更多)舞曲.有一些男孩女孩相互喜欢,而其他相互不喜欢(不会”单向喜欢“) ...

  3. [CQOI2009]跳舞 网络流

    题面:[CQOI2009]跳舞 题解: 首先最大时间不好求,而且数据范围很小,所以我们可以先二分一个最大时间,然后就只需要判断是否可行即可. 因此我们每二分一个mid,对于每个女生,连s ---> ...

  4. [BZOJ1305][CQOI2009]跳舞(网络流)

    1305: [CQOI2009]dance跳舞 Time Limit: 5 Sec  Memory Limit: 162 MBSubmit: 3944  Solved: 1692[Submit][St ...

  5. [CQOI2009]跳舞

    思路:二分答案+最大流.二分答案$m$,表示最多跳$m$轮.将每个人拆成两个点$a_i$$b_i$,$a_i$表示与任何人跳舞,$b_i$表示与不喜欢的人跳舞.对于第$i$个人,连一条从$a_i$到$ ...

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

    Description 一次舞会有n个男孩和n个女孩.每首曲子开始时,所有男孩和女孩恰好配成n对跳交谊舞.每个男孩都不会和同一个女孩跳两首(或更多)舞曲.有一些男孩女孩相互喜欢,而其他相互不喜欢(不会 ...

  7. 【[CQOI2009]跳舞】

    首先这种匹配类问题一看就是网络流了 之后想一想怎么搞 发现题目的意思是使得跳舞最少的男生跳的舞最多 很自然想到二分答案啊 现在转化成了一个判定性问题,能否使得所有人都跳上\(k\)只舞 由于喜欢和不喜 ...

  8. 题解 P1682 【过家家】

    P1682 过家家 题目描述 有2n个小学生来玩过家家游戏,其中有n个男生,编号为1到n,另外n个女生,编号也是1到n.每一个女生可以先选择一个和她不吵嘴的男生来玩,除此之外,如果编号为X的女生的朋友 ...

  9. AHOI2018训练日程(3.10~4.12)

    (总计:共90题) 3.10~3.16:17题 3.17~3.23:6题 3.24~3.30:17题 3.31~4.6:21题 4.7~4.12:29题 ZJOI&&FJOI(6题) ...

随机推荐

  1. 产品需求文档(PRD)的写作 【转】

    产品需求文档(PRD)的写作   一.文章的摘要介绍 无论我们做什么事都讲究方式方法,写产品需求文档(以下称PRD文档)也是如此,之前我通过四篇文章分享了自己写PRD文档的一些方法,而这一篇文章主要是 ...

  2. 选题博客:北航iCourse课程信息平台

    1. 用户调查 在选题的时候,我们面向北航所有本科在读本科生,发布了<北航信息平台用户调查>.此次问卷调查共回收有效问卷95份. 1.1 功能需求调查 调查其中一项是让同学们对平台功能进行 ...

  3. C++ Primer Plus学习:第十五章

    第十五章 友元.异常和其他 友元 友元类 表 0-1 class Tv { public: friend class Remote; } Remote类可以使用Tv的数据成员,Remote类在Tv类后 ...

  4. 1029对c语言文法的理解

    <程序>→<外部声明>|<程序><外部声明> <外部声明>→<函数定义>|<声明> <函数定义>→< ...

  5. C语言文法阅读与理解序

    <指针>→*  | * < 指针> <直接声明符>  <标识符> | <直接声明>[]| <直接声明>[常量表达式] | < ...

  6. jquery中on绑定click事件在苹果手机失效问题解决(巨坑啊)

    描述:用一个div写一个按钮,并给这个按钮添加一个点击事件,在安卓机器上一切正常,但是在苹果机型上会出现点击事件失效. <!DOCTYPE html> <html lang=&quo ...

  7. php中扩展pecl与pear

    要为大家分享的内容是PECL 和 PEAR 他们之间的不同和相同之处. PEAR 是“PHP Extension and Application Repository”的缩写,即PHP扩展和应用仓库. ...

  8. 知识点总结:Linq和Lambda

    基本语法: Linq:var result=from t in table order by sort ascending/descending select t: Lambda:var result ...

  9. 第110天:Ajax原生js封装函数

    一.Ajax的实现主要分为四部分: 1.创建Ajax对象 // 创建ajax对象 var xhr = null; if(window.XMLHttpRequest){ xhr = new XMLHtt ...

  10. 第109天:Ajax请求GET和POST的区别

    一.Ajax请求GET和POST的区别 1.使用Get请求时,参数在URL中显示,而使用Post方式,则不会显示出来 2.使用Get请求发送数据量小,Post请求发送数据量大 3.get请求需注意缓存 ...