HDU 3081 Marriage Match II(二分+并查集+最大流)

题意有N个女孩要与N个男孩玩配对游戏.每个女孩有一个可选男孩的集合(即该女孩可以选自己集合中的任意一个男孩作为该轮的搭档).然后从第一轮开始,每个女孩都要和一个不同的男孩配对.如果第一轮N个女孩都配对成功,那么就开始第二轮配对,女孩依然从自己的备选男孩集合中选择,但是不能选那些已经被该女孩在前几轮选择中选过的男孩了(比如i女孩在第一轮选了j男孩,那么i在第二轮就不能选j男孩了). 问你游戏最多能进行多少轮?

思路:建图:

          源点s为0,汇点t为2*n+1.女孩编号1到n,男孩编号n+1到2*n. 假设我们当前二分尝试的轮数为K(即能够进行K轮匹配),首先如果女孩i可能选择男孩j,那么就有边(i, j+n, 1).且源点到每个女孩i有边(s,i,K),每个男孩j到汇点t有边(j+n,t,K).如果最大流==K*n,那么就表示可以进行最少K轮匹配.对于女生之间有好友关系的,可以用并查集维护,或者是传递闭包,两个我都用了一下..时间一样..注意的是只合并女生

          证明转网上的

          证:如果满流,那么每个女生肯定选择了K个不同的男孩,每个男孩肯定被K个不同的女孩选择了(因为一个女孩到一个男孩边容量只为1,所以该女孩最多只能选该男孩一次).

          那么上面这样就能保证这个游戏可以进行K轮吗?可以的,假设当前图的流量为0,说明任何女孩都没选男孩. 你可以想象假如此时从S到所有女孩有流量1(虽然容量是K,但是目前我们只放出1流量)流出,那么这些流量肯定会汇集到t(因为最大流为K*n,而我们此时只不过n流量).这个汇集的过程就是第一轮女孩选择了各自不同男孩的结果. 现在从S到所有女孩又有流量1流出(即第二轮开始了),这些流量肯定又经过了n个男孩汇集到t点了 且 如果上一轮i女孩的流量走到j男孩,这一轮i女孩的流量肯定不走j男孩了(因为i女孩到j男孩的边只有1容量).

           综上所述,只要最大流==K*n,那么就能进行K轮.(如果能进行K轮配对,是不是最大流也一定==K*n?这个也是一定的,也是按照上面的模型过程模拟即可.它们互为充要条件)

           

#include <cstdio>
#include <queue>
#include <cstring>
#include <iostream>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <map>
#include <string>
#include <set>
#include <ctime>
#include <cmath>
#include <cctype>
using namespace std;
#define maxn 250
#define INF 1<<29
#define LL long long
int cas=1,T;
struct Edge
{
	int from,to,cap,flow;
	Edge(int u,int v,int c,int f):from(u),to(v),cap(c),flow(f){}
};
int n,m;
struct Dinic
{
//	int n,m;
    int s,t;
	vector<Edge>edges;        //边数的两倍
	vector<int> G[maxn];      //邻接表,G[i][j]表示结点i的第j条边在e数组中的序号
	bool vis[maxn];           //BFS使用
	int d[maxn];              //从起点到i的距离
	int cur[maxn];            //当前弧下标
	void init()
	{
	   for (int i=0;i<=n*2+1;i++)
		   G[i].clear();
	   edges.clear();
	}
	void AddEdge(int from,int to,int cap)
	{
		edges.push_back(Edge(from,to,cap,0));
		edges.push_back(Edge(to,from,0,0));        //反向弧
		int mm=edges.size();
		G[from].push_back(mm-2);
		G[to].push_back(mm-1);
	}
	bool BFS()
	{
		memset(vis,0,sizeof(vis));
		queue<int>q;
		q.push(s);
		d[s]=0;
		vis[s]=1;
		while (!q.empty())
		{
			int x = q.front();q.pop();
			for (int i = 0;i<G[x].size();i++)
			{
				Edge &e = edges[G[x][i]];
				if (!vis[e.to] && e.cap > e.flow)
				{
					vis[e.to]=1;
					d[e.to] = d[x]+1;
					q.push(e.to);
				}
			}
		}
		return vis[t];
	}

	int DFS(int x,int a)
	{
		if (x==t || a==0)
			return a;
		int flow = 0,f;
		for(int &i=cur[x];i<G[x].size();i++)
		{
			Edge &e = edges[G[x][i]];
			if (d[x]+1 == d[e.to] && (f=DFS(e.to,min(a,e.cap-e.flow)))>0)
			{
				e.flow+=f;
				edges[G[x][i]^1].flow-=f;
				flow+=f;
				a-=f;
				if (a==0)
					break;
			}
		}
		return flow;
	}

	int Maxflow(int s,int t)
	{
		this->s=s;
		this->t=t;
		int flow = 0;
		while (BFS())
		{
			memset(cur,0,sizeof(cur));
			flow+=DFS(s,INF);
		}
		return flow;
	}
}dc;
int pre[maxn];
int dis[maxn][maxn];
int Find(int x)
{
	return pre[x]==-1?x:pre[x]=Find(pre[x]);
}

bool solve(int k)
{
	dc.init();
	for (int i = 1;i<=n;i++)
	{
		dc.AddEdge(0,i,k);
		dc.AddEdge(n+i,n*2+1,k);
		for (int j = 1;j<=n;j++)
		  if (dis[i][j])
			  dc.AddEdge(i,j+n,1);
	}
	return dc.Maxflow(0,n*2+1)==k*n;
}
int main()
{
	scanf("%d",&T);
	while (T--)
	{
		int f;
		memset(dis,0,sizeof(dis));
		memset(pre,-1,sizeof(pre));
		scanf("%d%d%d",&n,&m,&f);
        for (int i = 1;i<=m;i++)
		{
			int u,v;
			scanf("%d%d",&u,&v);
			dis[u][v]=1;
		}
		for (int i = 1;i<=f;i++)
		{
			int u,v;
			scanf("%d%d",&u,&v);
			int uu = Find(u);
			int vv = Find(v);
			if (uu!=vv)
				pre[uu]=vv;
		}
		for (int i = 1;i<=n;i++)
			for (int j = i+1;j<=n;j++)
				if (Find(i)==Find(j))
					for (int k=1;k<=n;k++)
					{
						dis[i][k]=dis[j][k]=(dis[i][k] || dis[j][k]);
					}
		int l = 0;
		int r = 100;
		while (l<=r)
		{
			int mid = (l+r)/2;
			if (solve(mid))
				l = mid+1;
			else
				r=mid-1;
		}
		printf("%d\n",r);
	}
}



/*没有用并查集....时间一样...*/
/*
#include <cstdio>
#include <queue>
#include <cstring>
#include <iostream>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <map>
#include <string>
#include <set>
#include <ctime>
#include <cmath>
#include <cctype>
using namespace std;
#define maxn 250
#define INF 1<<29
#define LL long long
int cas=1,T;
struct Edge
{
	int from,to,cap,flow;
	Edge(int u,int v,int c,int f):from(u),to(v),cap(c),flow(f){}
};
int n,m;
struct Dinic
{
//	int n,m;
    int s,t;
	vector<Edge>edges;        //边数的两倍
	vector<int> G[maxn];      //邻接表,G[i][j]表示结点i的第j条边在e数组中的序号
	bool vis[maxn];           //BFS使用
	int d[maxn];              //从起点到i的距离
	int cur[maxn];            //当前弧下标
	void init()
	{
	   for (int i=0;i<=n*2+1;i++)
		   G[i].clear();
	   edges.clear();
	}
	void AddEdge(int from,int to,int cap)
	{
		edges.push_back(Edge(from,to,cap,0));
		edges.push_back(Edge(to,from,0,0));        //反向弧
		int mm=edges.size();
		G[from].push_back(mm-2);
		G[to].push_back(mm-1);
	}
	bool BFS()
	{
		memset(vis,0,sizeof(vis));
		queue<int>q;
		q.push(s);
		d[s]=0;
		vis[s]=1;
		while (!q.empty())
		{
			int x = q.front();q.pop();
			for (int i = 0;i<G[x].size();i++)
			{
				Edge &e = edges[G[x][i]];
				if (!vis[e.to] && e.cap > e.flow)
				{
					vis[e.to]=1;
					d[e.to] = d[x]+1;
					q.push(e.to);
				}
			}
		}
		return vis[t];
	}

	int DFS(int x,int a)
	{
		if (x==t || a==0)
			return a;
		int flow = 0,f;
		for(int &i=cur[x];i<G[x].size();i++)
		{
			Edge &e = edges[G[x][i]];
			if (d[x]+1 == d[e.to] && (f=DFS(e.to,min(a,e.cap-e.flow)))>0)
			{
				e.flow+=f;
				edges[G[x][i]^1].flow-=f;
				flow+=f;
				a-=f;
				if (a==0)
					break;
			}
		}
		return flow;
	}

	int Maxflow(int s,int t)
	{
		this->s=s;
		this->t=t;
		int flow = 0;
		while (BFS())
		{
			memset(cur,0,sizeof(cur));
			flow+=DFS(s,INF);
		}
		return flow;
	}
}dc;

int dis[maxn][maxn];
bool solve(int k)
{
	dc.init();
	for (int i = 1;i<=n;i++)
	{
		dc.AddEdge(0,i,k);
		dc.AddEdge(n+i,n*2+1,k);
		for (int j = 1;j<=n;j++)
		  if (dis[i][j])
			  dc.AddEdge(i,j+n,1);
	}
	return dc.Maxflow(0,n*2+1)==k*n;
}
int main()
{
	scanf("%d",&T);
	while (T--)
	{
		int f;
		memset(dis,0,sizeof(dis));
		memset(pre,-1,sizeof(pre));
		scanf("%d%d%d",&n,&m,&f);
        for (int i = 1;i<=m;i++)
		{
			int u,v;
			scanf("%d%d",&u,&v);
			dis[u][v]=1;
		}
		for (int i = 1;i<=f;i++)
		{
			int u,v;
			scanf("%d%d",&u,&v);
			for (int j = 1;j<=n;j++)
			{
				dis[u][j]=dis[v][j]=(dis[u][j] || dis[v][j]);
			}
		}

		int l = 0;
		int r = 100;
		while (l<=r)
		{
			int mid = (l+r)/2;
			if (solve(mid))
				l = mid+1;
			else
				r=mid-1;
		}
		printf("%d\n",r);
	}
}*/


Description

Presumably, you all have known the question of stable marriage match. A girl will choose a boy; it is similar as the game of playing house we used to play when we are kids. What a happy time as so many friends playing together. And it is normal that a fight or a quarrel breaks out, but we will still play together after that, because we are kids. 
Now, there are 2n kids, n boys numbered from 1 to n, and n girls numbered from 1 to n. you know, ladies first. So, every girl can choose a boy first, with whom she has not quarreled, to make up a family. Besides, the girl X can also choose boy Z to be her boyfriend when her friend, girl Y has not quarreled with him. Furthermore, the friendship is mutual, which means a and c are friends provided that a and b are friends and b and c are friend. 
Once every girl finds their boyfriends they will start a new round of this game―marriage match. At the end of each round, every girl will start to find a new boyfriend, who she has not chosen before. So the game goes on and on. 
Now, here is the question for you, how many rounds can these 2n kids totally play this game? 
 

Input

There are several test cases. First is a integer T, means the number of test cases. 
Each test case starts with three integer n, m and f in a line (3<=n<=100,0<m<n*n,0<=f<n). n means there are 2*n children, n girls(number from 1 to n) and n boys(number from 1 to n). 
Then m lines follow. Each line contains two numbers a and b, means girl a and boy b had never quarreled with each other. 
Then f lines follow. Each line contains two numbers c and d, means girl c and girl d are good friends. 
 

Output

For each case, output a number in one line. The maximal number of Marriage Match the children can play.
 

Sample Input

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

Sample Output

        
        
        
        
2
 


你可能感兴趣的:(HDU 3081 Marriage Match II(二分+并查集+最大流))