题目背景
二分图
感谢@一扶苏一 提供的hack数据
题目描述
给定一个二分图,结点个数分别为n,m,边数为e,求二分图最大匹配数
输入输出格式
输入格式:
第一行,n,m,e
第二至e+1行,每行两个正整数u,v,表示u,v有一条连边
输出格式:
共一行,二分图最大匹配
输入输出样例
输入样例#1: 复制
1 1 1 1 1
输出样例#1: 复制
1
说明
n,m \leq 1000n,m≤1000, 1 \leq u \leq n1≤u≤n, 1 \leq v \leq m1≤v≤m
因为数据有坑,可能会遇到 v>mv>m 的情况。请把 v>mv>m 的数据自觉过滤掉。
算法:二分图匹配
今天又重新看了匈牙利算法的实现以及思想,突然恍然大悟。其实很早就接触了这个算法,但是一直没有太理解。今天又重新看了一遍,就明白了。所以说,如果看了好久还是感到很迷惑就先放到一边,等有一定积累的时候再回头看的时候很快就明白了。
算法思想:匈牙利算法的思想就是为当前点的所连节点的pre寻找增广路,当然这里的增广路不是网络流里边的增广路,这里百度百科上讲的很好。
求二分图最大匹配可以用最大流或者匈牙利算法。
最大匹配
给定一个二分图G,在G的一个子图M中,M的边集中的任意两条边都不依附于同一个顶点,则称M是一个匹配.
选择这样的边数最大的子集称为图的最大匹配问题(maximal matching problem)
如果一个匹配中,图中的每个顶点都和图中某条边相关联,则称此匹配为完全匹配,也称作完备匹配。
算法
求最大匹配的一种显而易见的算法是:先找出全部匹配,然后保留匹配数最多的。但是这个算法的复杂度为边数的指数级函数。因此,需要寻求一种更加高效的算法。
增广路的定义(也称增广轨或交错轨):
若P是图G中一条连通两个未匹配顶点的路径,并且属M的边和不属M的边(即已匹配和待匹配的边)在P上交替出现,则称P为相对于M的一条增广路径.
由增广路的定义可以推出下述三个结论:
1-P的路径长度必定为奇数,第一条边和最后一条边都不属于M.
2-P经过取反操作可以得到一个更大的匹配M'.
3-M为G的最大匹配当且仅当不存在相对于M的增广路径.
以上内容来自百度百科。
就像这样,容易看出来从x3->y2->x1->y3.不属于与属于m的边交替出现,且起点和终点都是未盖点。
#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
using namespace std;
#define rep(i,j,k) for(int i=j;i<=k;i++)
vector<int>mp[1005];
bool vis[1005];
int pre[1005];
bool dfs(int now)
{
for(int i=0;i<mp[now].size();i++)
{
int tmp=mp[now][i];
if(!vis[tmp])
{
vis[tmp]=true;
if(!pre[tmp]||dfs(pre[tmp]))
{
pre[tmp]=now;
return true;
}
}
}
return false;
}
int main()
{
int n,m,e;
scanf("%d%d%d",&n,&m,&e);
rep(i,1,e)
{
int u,v;
scanf("%d%d",&u,&v);
if(v>m)continue;
mp[u].push_back(v);
}
int ans=0;
rep(i,1,n)
{
memset(vis,false,sizeof(vis));
if(dfs(i))ans++;
}
cout<<ans<<endl;
}