二分图的定义
将一个图的所有顶点划分为两个不相交集和,使得图中的每一条边的顶点分别属于点集合和点集,即同一点集中的点不构成边,这样的图叫做二分图。
维基百科中给出的无向图的二分图的充分必要条件是:至少有两个顶点,且其所有回路的长度均为偶数。
二分图中增广路的定义
如果一条路径的首尾都是非匹配点,路径中除此之外(如果有)其他的点均是匹配点,那么这条路径就是一条增广路经
匈牙利算法
- 建立有向图,分为二分图的左右两侧
- 优先选择左侧序号更小的进行匹配。
- 如果左侧两个点的目标点冲突,让序号小的点选择另一个可能的目标点进行匹配。如果没有可匹配的其他点,则匹配失败。
优化
与传统算法类似。省略重置数组的操作,而用来表示现在搜索到第几个左侧点,并用它来标记数组。
#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酒店之王