[CQOI2009]跳舞

link

一道网络流建模题目,挺基础的,但由于本人世面见得太少(说白了就是题做得太少),考场上没有看出来它是一个网络流。这道题目让我知道了看似和网络流搭不上边的题目竟然也可以用它来解决,啊世界奇妙。

说回这道题。由于题目要求每个人在每一局舞会中都不能袖手旁观,所以我们需要找出一个匹配使得每个人都恰好有m个人为伴,这是基础条件,不满足就不合法。原因嘛,很容易想到假如每个人都有刚好那么多个舞伴,一定可以构造出一种顺序使得每个人每一局都不再孤单。而恰好这个词需要上界和下界来限制(差分约束系统吗……),上界可以考虑给每个人从源点连的边的流量上界来体现,下界就直接看最大流有没有把它跑满。但这样一来我们就只能做到对一个可能答案的检查,于是就可以想到用二分答案来解决。虽然此题数据范围小到二分答案似乎并没有什么卵用。

至于限制不喜欢的人数,可以用拆点的思想处理,把每个人分裂成喜欢和不喜欢两部分分别连接,两个点连到一个公共点,共用流量上界即可。

#include<cstdio>
#include<cstring>
#define zczc
using namespace std;
const int N=55;
const int maxn=1e9;
inline void read(int &wh){
	wh=0;int f=1;char w=getchar();
	while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
	while(w>='0'&&w<='9'){wh=wh*10+w-'0';w=getchar();}
	wh*=f;return;
}
inline int min(int s1,int s2){
	return s1<s2?s1:s2;
}

int cnt,s,t,a[N][3],b[N][3],m,n;
char w[N];
bool aa[N][N];

struct edge{
	int t,v,next;
}e[N*N*N];
int esum=1,head[N*N*N];
inline void adde(int fr,int to,int val){
	e[++esum]=(edge){to,val,head[fr]};head[fr]=esum;
}
inline void add(int fr,int to,int val){
	printf("%d %d %d\n",fr,to,val);
	adde(fr,to,val);adde(to,fr,0);
}

int d[N*N],q[N*N],l,r;
bool check(int wh){
	memset(d,0,sizeof(d));
	d[q[l=r]=s]=1;
	while(l<=r)
		for(int i=head[wh=q[l++]],th;i;i=e[i].next)
			if(e[i].v&&d[th=e[i].t]==0)d[th]=d[wh]+1,q[++r]=th;
	return d[t];
}
int dinic(int wh,int val){
	if(wh==t)return val;
	int cost=0;
	for(int i=head[wh],th;i;i=e[i].next){
		if(e[i].v==0||d[th=e[i].t]!=d[wh]+1)continue;
		int now=dinic(th,min(val,e[i].v));
		if(now)val-=now,cost+=now,e[i].v-=now,e[i^1].v+=now;
	}
	return d[wh]=val?0:d[wh],cost;
}
bool solve(int wh){
	memset(head,0,sizeof(head));esum=1;
	for(int i=1;i<=m;i++){
		add(s,a[i][0],wh);
		add(b[i][0],t,wh);
		add(a[i][0],a[i][1],maxn);
		add(a[i][0],a[i][2],n);
		add(b[i][1],b[i][0],maxn);
		add(b[i][2],b[i][0],n);
		for(int j=1;j<=m;j++){
			if(aa[i][j])add(a[i][1],b[j][1],1);
			else add(a[i][2],b[j][2],1);
		}
	}
	int an=0;
	while(check(0))an+=dinic(s,maxn);
	return an==wh*m;
}

signed main(){
	
	#ifdef zczc
	freopen("in.txt","r",stdin);
	#endif
	
	read(m);read(n);
	for(int i=1;i<=m;i++){
		scanf("%s",w);
		for(int j=0;j<m;j++)aa[i][j+1]=w[j]=='Y';
	}
	int l=0,r=m,mid;
	s=++cnt;t=++cnt;
	for(int i=1;i<=m;i++){
		for(int j=0;j<3;j++)a[i][j]=++cnt,b[i][j]=++cnt;
	}
	while(l<r)solve(mid=l+r+1>>1)?l=mid:r=mid-1;
	printf("%d\n",l);
	
	return 0;
}
posted @   Feyn618  阅读(33)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示