题解 P3153 【[CQOI2009]跳舞】
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]跳舞】的更多相关文章
- [洛谷P3153] [CQOI2009]跳舞
题目大意:有n个女生,n个男生,每次一男一女跳舞.同一队只会跳一次.每个男孩最多只愿意和k个不喜欢的女孩跳舞,女孩同理.问舞会最多能有几首舞曲? 题解:二分跳了多少次舞,每次重建图,建超级原点和汇点, ...
- P3153 [CQOI2009]跳舞
题目描述 一次舞会有n个男孩和n个女孩.每首曲子开始时,所有男孩和女孩恰好配成n对跳交谊舞.每个男孩都不会和同一个女孩跳两首(或更多)舞曲.有一些男孩女孩相互喜欢,而其他相互不喜欢(不会”单向喜欢“) ...
- [CQOI2009]跳舞 网络流
题面:[CQOI2009]跳舞 题解: 首先最大时间不好求,而且数据范围很小,所以我们可以先二分一个最大时间,然后就只需要判断是否可行即可. 因此我们每二分一个mid,对于每个女生,连s ---> ...
- [BZOJ1305][CQOI2009]跳舞(网络流)
1305: [CQOI2009]dance跳舞 Time Limit: 5 Sec Memory Limit: 162 MBSubmit: 3944 Solved: 1692[Submit][St ...
- [CQOI2009]跳舞
思路:二分答案+最大流.二分答案$m$,表示最多跳$m$轮.将每个人拆成两个点$a_i$$b_i$,$a_i$表示与任何人跳舞,$b_i$表示与不喜欢的人跳舞.对于第$i$个人,连一条从$a_i$到$ ...
- 1305. [CQOI2009]跳舞【最大流+二分】
Description 一次舞会有n个男孩和n个女孩.每首曲子开始时,所有男孩和女孩恰好配成n对跳交谊舞.每个男孩都不会和同一个女孩跳两首(或更多)舞曲.有一些男孩女孩相互喜欢,而其他相互不喜欢(不会 ...
- 【[CQOI2009]跳舞】
首先这种匹配类问题一看就是网络流了 之后想一想怎么搞 发现题目的意思是使得跳舞最少的男生跳的舞最多 很自然想到二分答案啊 现在转化成了一个判定性问题,能否使得所有人都跳上\(k\)只舞 由于喜欢和不喜 ...
- 题解 P1682 【过家家】
P1682 过家家 题目描述 有2n个小学生来玩过家家游戏,其中有n个男生,编号为1到n,另外n个女生,编号也是1到n.每一个女生可以先选择一个和她不吵嘴的男生来玩,除此之外,如果编号为X的女生的朋友 ...
- 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题) ...
随机推荐
- Professional Books
Machine Learning: Pattern Recognition and Machine Learning(PRML) https://mqshen.gitbooks.io/p ...
- centos上搭建git服务--4
Git是目前世界上最先进的分布式版本控制系统(没有之一).使用Svn的请参考<版本控制-svn服务器搭建和常用命令(centos 6.3)>,下面介绍Git的常用命令 常用命令 简单版 升 ...
- preg_replace 以及弃用的e
preg_replace (PHP 4, PHP 5) preg_replace — 执行一个正则表达式的搜索和替换 说明¶ mixed preg_replace ( mixed $pattern , ...
- TCP系列45—拥塞控制—8、SACK关闭的拥塞撤销与虚假快速重传
一.概述 这篇文章介绍一下TCP从Recovery状态恢复到Open状态的时候cwnd的更新.我们在tcp重传部分的文章中曾经介绍过虚假重传的概念,Linux在探测到虚假重传的时候就会执行拥塞撤销操作 ...
- 201621123037 《Java程序设计》第6周学习总结
作业06-接口.内部类 1. 本周学习总结 1.1 面向对象学习暂告一段落,请使用思维导图,以封装.继承.多态为核心概念画一张思维导图或相关笔记,对面向对象思想进行一个总结. 注1:关键词与内容不求多 ...
- 软工网络15团队作业4-DAY7
每日例会 昨天的工作. 张陈东芳:sql连接的基本完成,尝试被其他类调用,未导入全部商品信息: 吴敏烽:基本完成商品信息的调用: 周汉麟:设定商品的调用规则: 林振斌:设计缓存区代码,用于存取最近浏览 ...
- 【第二周】Java实现英语文章词频统计
1.需求:对于给定的英文文章进行单词频率的统计 2.分析: (1)建立一个如下图所示的数据库表word_frequency用来存放单词和其对应数量 (2)Scanner输入要查询的英文文章存入Stri ...
- HDU 2105 The Center of Gravity
http://acm.hdu.edu.cn/showproblem.php?pid=2105 Problem Description Everyone know the story that how ...
- c 结构体读取与保存
1.结构体保存到文本 #include <stdio.h> #include <stdlib.h> #include <string.h> #define max ...
- APDU命令与响应格式【转】
本文转载自:http://map.im/apduintroduce 命令格式 APDU命令由命令头和命令体组成: CLA | INS | P1 | P2 | Lc | DATA | Le命令头: CL ...