【题解】过家家

过家家

题目描述

\(2n\) 个小学生来玩过家家游戏,其中有 \(n\) 个男生,编号为 \(1\)\(n\),另外 \(n\) 个女生,编号也是 \(1\)\(n\)。每一个女生可以先选择一个和她不吵嘴的男生来玩,除此之外,如果编号为 \(X\) 的女生的朋友(也是女生,且编号为 \(Y\))不和编号为 \(Z\) 的男生吵嘴,那么 \(X\) 也可以选择 \(Z\)。此外,朋友关系是可以传递的,比如 \(a\)\(b\) 是朋友,\(b\)\(c\) 是朋友,那么我们可以认为 \(a\)\(c\) 也是朋友。

当每一位女生都选择了玩伴,那么他们会开始新一轮游戏。在每一轮后,每个女生都会开始去找一个新的男生做玩伴(以前没选过)。而且每一个女生最多能强制 \(k\) 个男生接受,无论他们以前是否吵嘴。

现在你的任务就是确定这 \(2n\) 个小学生最多能玩几轮游戏。

输入格式

第一行有四个整数 \(n,m,k,f\)\(3 \le n \le 250\)\(0 < m < n^{2}\)\(0 \le f < n\))。

\(n\) 表示有 \(2n\) 个小学生,其中 \(n\) 个男生 \(n\) 个女生。

接下来 \(m\) 行,每行包含两个数字 \(a,b\) 表示编号为 \(a\) 的女生和编号为 \(b\) 的男生从没吵嘴过。

再接下来 \(f\) 行,每行包含两个数字 \(c,d\) 表示编号为 \(c\) 的女生和编号为 \(d\) 的女生是朋友。

输出格式

对于每组数据,输出一个整数,表示 \(2n\) 个小学生最多能玩几轮。

样例 #1

样例输入 #1

4 5 1 2
1 1
2 3
3 2
4 2
4 4
1 4
2 3

样例输出 #1

3

算法1:
并查集

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
int p[70000];
int n,m,k,f,cnt[70000],vis[1000][1000];
typedef struct node
{
	int x,y;
}node;
int find(int x)
{
	if(x!=p[x])
	{
		p[x]=find(p[x]);
	}
	return p[x];
}

void merge(int x,int y)
{
	int fa=find(x),fb=find(y);
	if(fa!=fb)
	{
		p[fb]=fa;
	}
}
node a[70000];
int main()
{
    
	cin>>n>>m>>k>>f;
	for(int i=1;i<=n;i++) p[i]=i;
		
	for(int i=1;i<=m;i++)
	{
		cin>>a[i].x>>a[i].y;
	}
	int aa,bb;
	for(int i=1;i<=f;i++)
	{
		cin>>aa>>bb;
		merge(aa,bb);
	}
	
	for(int i=1;i<=m;i++)
	{
		int pa=find(a[i].x),pb=a[i].y;
		if(!vis[pa][pb])
		{
			cnt[pa]++;
			vis[pa][pb]=1;
		}
	}
	int ans=0x3f3f3f3f;
	for(int i=1;i<=n;i++)
	{
		if(cnt[i]) ans=min(ans,cnt[i]);
	}
	printf("%d",min(ans+k,n));
	return 0;
}

算法2:
二分+最大流

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
int n,m,k,f,S,T;
const int N=20010,M=200010,inf=1e9;
int dance[1500][1500];

inline int min(int x,int y)
{
	return x>y?y:x;
}

namespace Dinic
{
	int f[M],ne[M],e[M];
	int h[N],d[N],q[N],idx,cur[N];
	
	inline void init()
	{
		memset(h,-1,sizeof h);
		idx=0;
	}
	
	inline void add(int a,int b,int c)
	{
		e[idx]=b,f[idx]=c,ne[idx]=h[a],h[a]=idx++;
		e[idx]=a,f[idx]=0,ne[idx]=h[b],h[b]=idx++;
	}
	
	inline bool bfs()
	{
		int tt=0,hh=0;
		memset(d,-1,sizeof d);
		q[0]=S,d[S]=0,cur[S]=h[S];
		
		while(hh<=tt)
		{
			int t=q[hh++];
			for(int i=h[t];~i;i=ne[i])
			{
				int ver=e[i];
				if(d[ver]==-1&&f[i])
				{
					d[ver]=d[t]+1;
					cur[ver]=h[ver];
					if(ver==T) return true;
					q[++tt]=ver;
				}
			}
		}
		return false;
	}
	
	inline int find(int u,int limits)
	{
		if(u==T) return limits;
		int flow=0;
		
		for(int i=cur[u];~i&&flow<limits;i=ne[i])
		{
			int ver=e[i];
			cur[u]=i;
			if(d[ver]==d[u]+1&&f[i])
			{
				int t=find(ver,min(f[i],limits-flow));
				if(!t) d[ver]=-1;
				f[i]-=t;
				f[i^1]+=t;
				flow+=t;
			}
		}
		return flow;
	}
	
	inline int dinic()
	{
		int ans=0,r;
		while(bfs())
		{
			while(r=find(S,inf)) ans+=r;
		}
		return ans;
	}
}

inline void floyd()
{
	for(int k=n+1;k<=2*n;k++)
	{
		for(int i=1;i<=n;i++)//男
		{
			for(int j=n+1;j<=2*n;j++)
			{
				if(dance[i][k]&&dance[k][j])
				{
					dance[i][j]|=(dance[i][k]&&dance[k][j]);
				}
			}
		}
	}
}

inline bool check(int mid)
{
	Dinic::init();
	for(int i=1;i<=n;i++)
	{
		Dinic::add(S,i,mid);
		Dinic::add(2*n+i,T,mid);
		Dinic::add(n+i,2*n+i,k);	
	}
	
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			if(dance[i][n+j]) Dinic::add(i,2*n+j,1);
			else Dinic::add(i,n+j,1);
		}
	}
	return Dinic::dinic()==n*mid;
}
int main()
{
	std::cin>>n>>m>>k>>f;
	S=3*n+20,T=S+1;
	for(int i=1;i<=m;i++)
	{
		int u,v;
		std::cin>>u>>v;
		dance[v][u+n]=1;
	}
	for(int i=1;i<=f;i++)
	{
		int u,v;
		std::cin>>u>>v;
		dance[u+n][v+n]=dance[v+n][u+n]=1;
	}
	floyd();
	int l=0,r=k+2*n,mid;
	while(l<r)
	{
		mid=(l+r+1)>>1;
		if(check(mid)) l=mid;
		else r=mid-1;
	}
	printf("%d",l);
	return 0;
}
posted @ 2022-10-24 14:17  watasky  阅读(27)  评论(0编辑  收藏  举报