首页 > 其他分享 >HDU 3081 Marriage Match II(二分+并查集+最大流)

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

时间:2023-06-12 14:34:33浏览次数:47  
标签:HDU int 查集 flow 男孩 II 女孩 maxn include


题意:有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,int,查集,flow,男孩,II,女孩,maxn,include
From: https://blog.51cto.com/u_16156555/6462549

相关文章

  • HDU 1556 Color the ball(树状数组区间更新)
    题意:中文题思路:一道区间更新,单点查询的裸题,用线段树做更好,因为还没看到所以这里用树状数组做。树状数组标记区间的方法很特别,比如给区间[a,b]内的气球涂颜色时,我们add(a,1),add(b+1,-1),单点查询的时候sum(x)就是x这个气球被涂色的总次数。建议先在纸上自己试一下看看,有点抽象,可以这......
  • hdu2084数塔(DP)
    思路:简单的数塔DP#include<stdio.h>#include<string.h>#include<algorithm>usingnamespacestd;inta[105][105],dp[105][105];intmain(){intt,n,i,j;scanf("%d",&t);while(t--){scanf("%d",......
  • HDU 5491 The Next(构造)
    题意:给你D(D<=2^31),s1和s2,求比D大的第一个数并且这个数二进制1的数目在[s1,s2]范围里,输出这个数思路:直接从D+1算起,如果1的数目>s2那就跳过,如果s1>sum1,那么就将这个数的低位s1-sum1个0补成1,那么就是最优的了#include<bits/stdc++.h>usingnamespacestd;#defineLLlonglongint......
  • HDU 5489 Removed Interval(DP)
    题意:求去掉某一个长度为L的子串的LIS思路:画画图其实比较显然的想法是去掉这个区间的时候答案是右边以第一个数开头的LIS+左边最后一个数小于右边第一个数的LIS,为什么是右边以第一个数开头的LIS呢,因为如果是在这个L的后第二个是最佳答案的话那么我在这个“窗口”滑到L+1这个位置的时......
  • HDU 5492 Find a path(DP)
    思路:将式子化开其实就是求(n+m-1)*s1-s2的最小值,s1表示各个格子的平方和,s2表示和的平方,留意到数据范围较小,令dp[i][j][k]为走到第i行第j列当前和为k的平方和的最小值,最后答案就是(n+m-1)*dp[i][j][k]-k*k#include<bits/stdc++.h>usingnamespacestd;#defineinf1e9inta[33][3......
  • Windows服务器IIS日志存放位置 及 查看方法
    用户每打开一次网页,iis都会记录用户IP、访问的网页地址、访问时间、访问状态等信息,这些信息保存在iis日志文件里,方便网站管理员掌握网页被访问情况和iis服务器运行情况。如果网页被恶意访问(如注入数据库),日志中会有相应的记录,并且能看到注入者用什么代码注入,便于分析网站漏洞。i......
  • 503. 下一个更大元素 II
    给定一个循环数组nums(nums[nums.length-1]的下一个元素是nums[0]),返回nums中每个元素的下一个更大元素。数字x的下一个更大的元素是按数组遍历顺序,这个数字之后的第一个比它更大的数,这意味着你应该循环地搜索它的下一个更大的数。如果不存在,则输出-1。示例1......
  • deal.II — an open source finite element library
    简介:Whatitis: AC++softwarelibrarysupportingthecreationoffiniteelementcodesandanopencommunityofusersanddevelopers.(Learnmore.)Mission: Toprovidewell-documentedtoolstobuildfiniteelementcodesforabroadvarietyofPDEs,froml......
  • 访问利用windows IIS 搭建的webdav出现500、403等代码的解决方案
    服务端在IIS中启用webDav添加创作规则(如第1张图)启用「身份验证」(如第2、3张图)防火墙设置将「在IIS中对该webDav站点设置的端口」设为「例外」或直接关闭防火墙重启该IIS站点(可选)客户端下载地址(选一即可):Windows64位https://www.123pan.com/s/FfztVv-DxNn3.html......
  • 第四天打卡|24. 两两交换链表中的节点 ● 19.删除链表的倒数第N个节点 面试题 02.07.
    24.两两交换链表中的节点:简单的交换 19.删除链表的倒数第N个节点: ●  面试题 02.07. 链表相交:这题没看过答案真的写不出来。太巧妙了  142.环形链表II:这题写过但是忘记怎么解的了还是看的答案。下次不能忘记  ......