二分图最大匹配 匈牙利算法

521 阅读2分钟

二分图的定义

将一个图的所有顶点划分为两个不相交集UUVV,使得图中的每一条边的顶点分别属于点集合UU和点集VV,即同一点集中的点不构成边,这样的图叫做二分图。
维基百科中给出的无向图GG的二分图的充分必要条件是:GG至少有两个顶点,且其所有回路的长度均为偶数。

二分图中增广路的定义

如果一条路径的首尾都是非匹配点,路径中除此之外(如果有)其他的点均是匹配点,那么这条路径就是一条增广路经

匈牙利算法

  1. 建立有向图GG,分为二分图的左右两侧
  2. 优先选择左侧序号更小的进行匹配。
  3. 如果左侧两个点的目标点冲突,让序号小的点选择另一个可能的目标点进行匹配。如果没有可匹配的其他点,则匹配失败。

优化

与传统算法类似。省略memsetmemset重置visvis数组的操作,而用nownow来表示现在搜索到第几个左侧点,并用它来标记visvis数组。

#include<bits/stdc++.h>
using namespace std;
const int N=1000,M=1000;
int n,m,now,E,tot,ans;
struct Edge{
	int to,nxt;
}e[N*M];
int head[N],vis[N],chs[N];
inline int read()
{
	int ret=0;char ch=getchar();
	while(ch<'0' || ch>'9')ch=getchar();
	while(ch>='0' && ch<='9')ret=ret*10+ch-'0',ch=getchar();
	return ret;
}
inline void add_edge(int u,int v,int flag)
{
	e[++tot].nxt=head[u];e[tot].to=v;head[u]=tot;
	if(flag)add_edge(v,u,0);
}
int dfs(int u)
{
	for(int i=head[u];i;i=e[i].nxt)
	{
		int v=e[i].to;
		if(vis[v]==now)continue;    //使用now的优化
		vis[v]=now;
		if(!chs[v] || dfs(chs[v]))
		{
			chs[v]=u;
			return 1;
		}
	}
	return 0;
}
int main()
{
	n=read();m=read();E=read();
	for(int i=1,u,v;i<=E;++i)
	{
		u=read();v=read();
		if(u>m || v>m)continue;
		add_edge(u,v,0);
	}
	for(int i=1;i<=n;++i)
	{
		++now;      //当前搜索到的左侧顶点 以此维护vis数组
		if(dfs(i))++ans;
	}
	cout<<ans<<endl;
}

例题

  • P1894[USACO4.2]完美的牛栏The Perfect Stall
  • P2071座位安排
  • P1525关押罪犯(并查集做法)
  • P2756飞行员配对方案问题
  • P1129[ZJOI2007]矩阵游戏
  • P2055[ZJOI2009]假期的宿舍
  • P1640[SCOI2010]连续攻击游戏
  • P1402酒店之王