【题解】过家家
过家家
题目描述
有 \(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;
}