首页 > 其他分享 >【题解】P3153 [CQOI2009]跳舞

【题解】P3153 [CQOI2009]跳舞

时间:2022-12-12 16:35:52浏览次数:72  
标签:P3153 head int 题解 fro 男孩 walk tail CQOI2009

原题链接

[CQOI2009]跳舞

题目描述

一次舞会有 \(n\) 个男孩和 \(n\) 个女孩。

每首曲子开始时,所有男孩和女孩恰好配成 \(n\) 对跳交谊舞。每个男孩都不会和同一个女孩跳两首(或更多)舞曲。

有一些男孩女孩相互喜欢,而其他相互不喜欢(不会“单向喜欢”)。每个男孩最多只愿意和 \(k\) 个不喜欢的女孩跳舞,而每个女孩也最多只愿意和 \(k\) 个不喜欢的男孩跳舞。

给出每对男孩女孩是否相互喜欢的信息,舞会最多能有几首舞曲?

输入格式

第一行包含两个整数 \(n\) 和 \(k\)。

以下 \(n\) 行每行包含 \(n\) 个字符,每个字符只可能是 YN。第 \((i + 1)\) 行的第 \(j\) 个字符为 Y 当且仅当男孩 \(i\) 和女孩 \(j\) 相互喜欢。

输出格式

一行一个整数代表舞曲数目的最大值。

样例 #1

样例输入 #1

3 0
YYY
YYY
YYY

样例输出 #1

3

提示

数据规模与约定

  • 对于 \(100\%\) 的数据,保证 \(1\leq n\leq 50\),\(1\leq k\leq 30\)。

题解

答案明显满足单调性,于是直接每次重新构造网络,随便跑网络流。
网络流比较重要的一点:建图要包含所有信息

#include<bits/stdc++.h>
using namespace std;
inline int rd(){
	int f=1,j=0;
	char w=getchar();
	while(!isdigit(w)){
		if(w=='-')f=-1;
		w=getchar();
	}
	while(isdigit(w)){
		j=j*10+w-'0';
		w=getchar();
	}
	return f*j;
}

const int N=1001,M=100001;
int head[N],to[M],fro[M],flo[M],tail=1;
int n,m,dep[N];
char S[N];
bool like[51][51],walk[N];
int s,t,cnt,poi[101][2];

inline void adlin(int x,int y,int z){
	to[++tail]=y;
	fro[tail]=head[x];
	head[x]=tail;
	flo[tail]=z;
	to[++tail]=x;
	fro[tail]=head[y];
	head[y]=tail;
	flo[tail]=0;
	return ;
}
bool bfs(){
	for(int i=1;i<=cnt;i++)dep[i]=walk[i]=0;
	dep[s]=1;
	deque<int>lin;
	lin.push_back(s),walk[s]=true;
	while(!lin.empty()){
		int u=lin.front();lin.pop_front();
		for(int k=head[u];k;k=fro[k]){
			int x=to[k];
			if(!flo[k]||walk[x])continue;
			walk[x]=true,dep[x]=dep[u]+1;
			lin.push_back(x);
		}
	}
	return walk[t];
}
int dfs(int u,int last){
	if(!last||u==t)return last;
	int had=0;
	for(int k=head[u];k;k=fro[k]){
		int x=to[k],y;
		if(!flo[k]||dep[x]<=dep[u]||!(y=dfs(x,min(last,flo[k]))))continue;
		had+=y,last-=y;
		flo[k]-=y,flo[k^1]+=y;
	}
	return had;
}
bool pan(int K){
	for(int i=1;i<=cnt;i++)head[i]=0;
	tail=1;
	for(int i=1;i<=n;i++){
		adlin(s,poi[i][0],K),adlin(poi[i+n][0],t,K);
		adlin(poi[i][0],poi[i][1],m),adlin(poi[i+n][1],poi[i+n][0],m);
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			adlin(poi[i][like[i][j]^1],poi[j+n][like[i][j]^1],1);
		}
	}
	int ansn=0;
	while(bfs())ansn+=dfs(s,INT_MAX);
	return (ansn>=K*n);
}

signed main(){
	n=rd(),m=rd();
	s=++cnt,t=++cnt;
	for(int i=1;i<=n*2;i++)poi[i][0]=++cnt,poi[i][1]=++cnt;
	for(int i=1;i<=n;i++){
		scanf("%s",S+1);
		for(int j=1;j<=n;j++)like[i][j]=(S[j]=='Y');
	}
	int l=0,r=n,mid;
	while(l<=r){
		mid=(l+r)/2;
		if(!pan(mid))r=mid-1;
		else l=mid+1;
	}
	printf("%d",r);
	return 0;
}

标签:P3153,head,int,题解,fro,男孩,walk,tail,CQOI2009
From: https://www.cnblogs.com/T-water/p/16976402.html

相关文章

  • 怎么彻底解决Windows如何不进行更新——问题解决
    文章目录​​一、情况描述​​​​二、问题解决​​​​2.1方法一​​​​2.2方法二​​一、情况描述感觉最近一直在更新啊,如果一直不更新,就会没有关机选项,而更新的话,电......
  • 【题解】P2050 [NOI2012] 美食节
    [NOI2012]美食节题目描述CZ市为了欢迎全国各地的同学,特地举办了一场盛大的美食节。作为一个喜欢尝鲜的美食客,小M自然不愿意错过这场盛宴。他很快就尝遍了美食节所有......
  • 2022icpc杭州铜牌题题解
    A.ModuloRuinstheLegend\[求s、d,使\suma_i+sn+d\frac{n(n+1)}{2}\(\bmodm)最小\\设sum=\suma_i\(\bmodm),t=gcd(n,\frac{n(n+1)}{2})\\原式=sum+kt\(\bm......
  • Git merge 报错:* commits behind * branch 问题解决
    Git大家都用的很多,但是在多人开发中难免会遇到代码冲突问题,因为mergepullrequest的时候遇到很多次这个问题,所以今天特意来记录一下: 问题:在mergePR到主分支(master/......
  • 【题解】P2774 方格取数问题
    方格取数问题题目描述有一个\(m\)行\(n\)列的方格图,每个方格中都有一个正整数。现要从方格中取数,使任意两个数所在方格没有公共边,且取出的数的总和最大,请求出最大的......
  • 【题解】P3358 最长k可重区间集问题
    最长k可重区间集问题题目描述给定实直线\(\text{L}\)上\(n\)个开区间组成的集合\(\mathbf{I}\),和一个正整数\(k\),试设计一个算法,从开区间集合\(\mathbf{I}\)中选......
  • 洛谷 P1786 帮贡排序 题解
    原题链接P1786帮贡排序解析实现方法一看题:这不就是道排序吗?但是——用啥办法呢?这自带的排序方法,肯定是不能用了那么我们就来写一个cmp排序函数吧!但是——输出排......
  • 题解 洛谷 P2885 [USACO07NOV]Telephone Wire G
    1.题面描述题目链接给出\(n\)棵树的高度。你可以进行任意次操作,每次操作形如:把某棵树增高\(x\),代价为\(x^2\)(注意:不能将一棵树的高度减少)。在操作完之后,一个状态......
  • 题解 洛谷 P3594 [POI2015] WIL
    1.题面描述题目链接给定一个长度为\(n\)的序列,你有一次机会选中一段连续的长度不超过\(d\)的区间,将里面所有数字全部修改为\(0\)。请找到最长的一段连续区间,使得该......
  • 题解 CodeForces 940E Cashback
    1.题面描述题目链接给定长为\(n\)的序列和一个整数\(c\),你需要将其分为若干段。对于每一段,若其长度为\(k\),则可以删除其中前\(\lfloor\frac{k}{c}\rfloor\)小的......