20200411 省选模拟T3 Dance

T3 dance CQOI2009

【问题描述】 一次舞会有 n 个男孩和 n 个女孩。每首曲子开始时,所有男孩和女孩恰好配成 n 对跳交 谊舞。每个男孩都不会和同一个女孩跳两首(或更多)舞曲。 有一些男孩女孩相互喜欢,而其他相互不喜欢(不会“单向喜欢”)。每个男孩最多只愿 意和 k 个不喜欢的女孩跳舞,而每个女孩也最多只愿意和 k 个不喜欢的男孩跳舞。 给出每对男孩女孩是否相互喜欢的信息,舞会最多能有几首舞曲?
【输入格式】 第一行包含两个整数 n 和 k。以下 n 行每行包含 n 个字符,其中第 i 行第 j 个字符为’Y’ 当且仅当男孩 i 和女孩 j 相互喜欢。
【输出格式】 仅一个数,即舞曲数目的最大值。

思路(from sol):考虑把所有的男孩和女孩都拆成两个点,分别表示喜欢点和不喜欢点。 对于一对相互喜欢的男孩女孩,男孩的喜欢点向女孩的喜欢点建一条流量为 1 的边。 对于一对不互相喜欢的男孩女孩,男孩的不喜欢点向女孩的不喜欢点建一条流量为 1 的 边。 每一个男孩的喜欢点向不喜欢点建一条流量为 k 的边,每一个女孩的不喜欢点向喜欢点 建一条流量为 k 的边。 建立超级源点和超级汇点,超级源点向所有男孩的喜欢点建一条流量为 a 的边,所有女 孩的喜欢点向超级汇点建一条流量为 a 的点。 a 就是题目中的曲目数。 我们考虑 a 是单调的,所以 可以二分 a 的值。 至于判断,跑遍最大流求出最小割 sum,如果 sum==n×a,那么当前的曲目数是可行的。

网络流题细节确实多…

#include <bits/stdc++.h>
#define minn(a,b) ( ((a)>(b)) ? (b):(a) )
using namespace std;
const int INF=(int)1e7+50;
const int N=1e6+50;	
int n,k,st,ed,h[N<<2],tot,cur[N];
char g[100][100];
struct line{
	int v,nxt,w;
}e[N];
int d[N];
queue<int> q;
inline void add(int u,int v,int w){
	e[tot]=(line){v,h[u],w};
    h[u]=tot++;//
    e[tot]=(line){u,h[v],0};
    h[v]=tot++;
}
bool bfs_(){
	memset(d,0,sizeof(d));while(q.size())	q.pop();
	q.push(st);d[st]=1;
	while(!q.empty()){
		int x=q.front();q.pop();
		for(int i=h[x];i!=-1;i=e[i].nxt){
			int y=e[i].v;
			if(e[i].w&&!d[y]){
				q.push(y);
				d[y]=d[x]+1;
			}
		}
	}
	return d[ed];
}
inline int dfs_(int x,int flow){
	if(x==ed||!flow)return flow;
	int ret=0;
	for(int i=cur[x];i!=-1;i=e[i].nxt){
		int y=e[i].v;
		if(e[i].w&&d[y]==d[x]+1) {
			int del=dfs_(y,minn(flow,e[i].w));
            flow-=del;ret+=del;
            e[i].w-=del;e[i^1].w+=del;//
		}
	}
	return ret;
}
int dinic(){
	int ret=0;
	while(bfs_()){for(int i=st;i<=ed;++i)cur[i]=h[i];ret+=dfs_(st,INF);}
	return ret;
}
inline int read(){
	int cnt=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-f;c=getchar();}
	while(isdigit(c)){cnt=(cnt<<3)+(cnt<<1)+(c^48);c=getchar();}
	return cnt*f;
}
inline void build1(int mid){
	for(int i=1;i<=n;++i) {
		add(st,i,mid);
		add(i+n+n,ed,mid);
		add(i,i+n,k);
		add(i+3*n,i+(n<<1),k);
	}
}
inline void build2(){
	for(int i=1;i<=n;++i){
		for(int j=1;j<=n;++j){
			if(g[i][j]=='Y') add(i,j+n+n,1);
			else add(i+n,j+n+n+n,1);
		}
	}
}
inline void build(int mid){
	memset(h,-1,sizeof(h));tot=0;
	build1(mid);
	build2();
}
bool check(int mid){if(dinic()==mid*n)return 1;return 0;}
int main(){
//	freopen("dance.in","r",stdin);
//	freopen("dance.out","w",stdout); 
	n=read(),k=read();st=0,ed=4*n+1;
	for(int i=1;i<=n;++i) {scanf("%s",g[i]+1);}
	int l=0,r=n;int mid;
	while(l+1<r) {
		mid=(l+r)>>1;build(mid);
		if(check(mid)) l=mid; else r=mid;
	}
	build(r);
	if(check(r)) printf("%d",r);
	else printf("%d",l);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值