HDU - 3081 Marriage Match II(二分+并查集+最大流/匈牙利删边)

题目链接:点击查看

题目大意:n个男生和n个女生配对,配对规则如下:

  1. 每个女生都可以选择没有吵过架的男生匹配
  2. 若女生A的好朋友是女生B,且女生B没有和男生C吵过架,则女生A也可以和男生C匹配

现在问最多可以匹配多少轮,要求每轮必须完全匹配,且每个男生匹配的女生以及每个女生匹配的男生都不能重复,求最大匹配轮数

题目分析:这个题目有两个做法,我都稍微说一下吧,因为这个题目挂在了网络流的题集里,所以自然得用网络流试试啦。。这是一个经典的二分图匹配问题了,但是限制稍微多了些,对于条件2,我们可以用并查集来处理,要记得实时记录哪些边已经建过了,防止重复建边,对于最后答案的求法,因为答案最小为0,最大为n,所以我们可以二分匹配轮数,也就是判断最后汇点的流量是否等于匹配轮数与人数n之积,这样就能解决这个题了

其实也可以直接用匈牙利跑,毕竟n只有100,每跑一次匈牙利,记得删掉已经匹配的边,跑到不能跑为止就好啦

网络流+二分+并查集:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
    
typedef long long LL;
    
const int inf=0x3f3f3f3f;

const int N=210;

bool maze[N][N],vis[N][N];

int f[N];
 
struct Edge
{
	int to,w,next;
}edge[N*N];//边数
 
int head[N],cnt,st,ed,n,m,t;
 
void addedge(int u,int v,int w)
{
	edge[cnt].to=v;
	edge[cnt].w=w;
	edge[cnt].next=head[u];
	head[u]=cnt++;
	edge[cnt].to=u;
	edge[cnt].w=0;//反向边边权设置为0
	edge[cnt].next=head[v];
	head[v]=cnt++;
}
 
int d[N],now[N*N];//深度 当前弧优化
 
bool bfs(int s,int t)//寻找增广路
{
	memset(d,0,sizeof(d));
	queueq;
	q.push(s);
	now[s]=head[s];
	d[s]=1;
	while(!q.empty())
	{
		int u=q.front();
		q.pop();
		for(int i=head[u];i!=-1;i=edge[i].next)
		{
			int v=edge[i].to;
			int w=edge[i].w;
			if(d[v])
				continue;
			if(!w)
				continue;
			d[v]=d[u]+1;
			now[v]=head[v];
			q.push(v);
			if(v==t)
				return true;
		}
	}
	return false;
}
 
int dinic(int x,int t,int flow)//更新答案
{
	if(x==t)
		return flow;
	int rest=flow,i;
	for(i=now[x];i!=-1&&rest;i=edge[i].next)
	{
		int v=edge[i].to;
		int w=edge[i].w;
		if(w&&d[v]==d[x]+1)
		{
			int k=dinic(v,t,min(rest,w));
			if(!k)
				d[v]=0;
			edge[i].w-=k;
			edge[i^1].w+=k;
			rest-=k;
		}
	}
	now[x]=i;
	return flow-rest;
}
 
int solve(int st,int ed)
{
	int ans=0,flow;
	while(bfs(st,ed))
		while(flow=dinic(st,ed,inf))
			ans+=flow;
	return ans;
}

int find(int x)
{
	return f[x]==x?x:f[x]=find(f[x]);
}

void merge(int x,int y)
{
	int xx=find(x);
	int yy=find(y);
	if(xx!=yy)
		f[xx]=yy;
}

void init()//build里初始化:初始化网络流的边
{
	memcpy(vis,maze,sizeof(maze));
	memset(head,-1,sizeof(head));
	cnt=0;
}

void Init()//main里初始化:初始化邻接矩阵+并查集
{
	memset(maze,false,sizeof(maze));
	for(int i=1;i>w;
	while(w--)
	{
		Init();
		scanf("%d%d%d",&n,&m,&t);
		while(m--)
		{
			int x,y;
			scanf("%d%d",&x,&y);
			maze[x][y+n]=true;
		}
		while(t--)
		{
			int x,y;
			scanf("%d%d",&x,&y);
			merge(x,y);
		}
		int l=0,r=n,ans;
		while(l<=r)//二分轮数
		{
			int mid=l+r>>1;
			build(mid);
			if(solve(st,ed)==mid*n)
			{
				ans=mid;
				l=mid+1;
			}
			else
				r=mid-1;
		}
		printf("%d\n",ans);
	}
	
	
	
	
	



        
        
        
         
        
    return 0;
}

匈牙利删边:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
    
typedef long long LL;
    
const int inf=0x3f3f3f3f;

const int N=110;

bool maze[N][N],vis[N];

int match[N],f[N],n,m,t;

bool dfs(int x)
{
	for(int i=1;i<=n;i++)
	{
		if(!vis[i]&&maze[x][i])
		{
			vis[i]=true;
			if(!match[i]||dfs(match[i]))
			{
				match[i]=x;
				return true;
			}
		}
	}
	return false;
}

int find(int x)
{
	return f[x]==x?x:f[x]=find(f[x]);
}

void merge(int x,int y)
{
	int xx=find(x);
	int yy=find(y);
	if(xx!=yy)
		f[xx]=yy;
}

void init()
{
	memset(maze,false,sizeof(maze));
	for(int i=1;i>w;
	while(w--)
	{
		init();
		scanf("%d%d%d",&n,&m,&t);
		while(m--)
		{
			int x,y;
			scanf("%d%d",&x,&y);
			maze[x][y]=true;
		}
		while(t--)
		{
			int x,y;
			scanf("%d%d",&x,&y);
			merge(x,y);
		}
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)
				if(find(i)==find(j))
					for(int k=1;k<=n;k++)
						if(maze[i][k])
							maze[j][k]=true;
		int ans=0;
		while(1)
		{
			memset(match,0,sizeof(match));
			int cnt=0;
			for(int i=1;i<=n;i++)
			{
				memset(vis,false,sizeof(vis));
				if(dfs(i))
					cnt++;
			}
			if(cnt!=n)
				break;
			ans++;
			for(int i=1;i<=n;i++)//枚举男孩 
				maze[match[i]][i]=false;//删边 
		}
		printf("%d\n",ans);
	}
	
	
	
	
	



        
        
        
         
        
    return 0;
}

 

你可能感兴趣的:(图论,并查集,二分,并查集,最大流,二分)