二分图
二分图,又叫做偶图其概念及定理过于复杂,之前早就研究过
如果一张无向图的n个节点可以分成A,B两个非空集合,其中AB不相交,并且在同一集合内的点之间没有边相连,那么这张无向图为一个二分图,A,B分别是二分图的左部右部
二分图的判定
判定,就是说判断这个图是不是二分图
我们看一个定理
一个二分图不存在奇环(长度为奇数的环)
那么就可以使用染色法来判定,其思想大致为:尝试用黑白两种颜色标记标记图中的节点,当一个节点被标记后,他的相邻的节点应该被标记为相反的颜色,若过程中发生冲突了,说明存在奇环,如果到最后都没有矛盾,就说明这是一个二分图
vector<int> g[size];
int color[size];
bool dfs(int u)
{
for(int i=0;i<g[].size;i++)
{
int v=g[u][i];//获取联通的点
if(color[v]==color[u])
{
return false;//发生冲突,不是二分图
}
if(color[v]==-1)//没有访问
{
color[v]=!color[u];
//这里偶尔写成3-
if(!dfs(v))//递归
{
return false;
}
}
}
return true;//到最后都没有发生矛盾,二分图
}
匹配
任意两条边都没有公共顶点的边的集合叫做二分图的匹配
如图,红边的就是一个匹配
定义匹配边,匹配点,未匹配边,未匹配点,那么根据字面意思就能理解了
最大匹配
给定二分图中的所有匹配,这些匹配的边数为最大值,叫做最大匹配
完全匹配
就是说图中每一个节点都与图的边相连接那么这个匹配叫做完全匹配,即图中所有的点都是匹配点
不难理解,完全匹配就是最大匹配
就是说,对于一个二分图,左部分的每一个点都与右部分的的每一个点匹配
完美匹配
完美匹配就是说左部分与右部分的点数相同,如果存在一个匹配,包含左部分右部分的所有节点,就叫做完美匹配,也就是说完美匹配不一定是完全匹配,但是完全匹配一定是完美匹配
匈牙利算法
增广路,是指在二分图中存在一条路两个非匹配点路径path,然后使得匹配边和非匹配边在path上交互出现,那么这就是增广路,也叫交错路,那么交错路肯定有下面的性质:
1.它的长度是个奇数
2.1,3,5,7奇数条边是非匹配,2,4,6,8偶数边是匹配边
所以,二分图的一组匹配s是最大匹配,当且仅当图中不存在s的增广路
匈牙利算法,又叫做增广路算法,来计算二分图的最大的匹配,他的流程为:
1.S不是空的,所有的边都是未匹配边
2.寻找一个路径path,把路径上的所有边匹配状态取反,得到一个更大的匹配
3.重复第2步,直到图中不存在增广路
这就是匈牙利算法,不断地增广
具体地步骤:
1.从二分图地一个x部中取出一个未匹配地点u开始找到一个未被访问的点的相邻的点v,然后对这个v分两种情况
2.如果这个点v未匹配点,那么找到了
3.如果是匹配点,那么就找v的匹配点w,然后根据取反思想,将(w,v)变成未匹配的边(u,v)改成匹配,那么就找到了一条新的
bool find(int u)
{
if(book[u]) return 0;
book[u]=1;//标记
for(int v=1;v<=n;v++)
{
if(mp[u][v])//连通
{
if(m[v]==0||find(d[v]))//非匹配点并且能根据下一个找到一个路
{
m[u]=v;
m[v]=u;
return 1;
}
}
}
}
最后来一个综合代码
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#define N 2010
using namespace std;
int n,m,e,ans;
int vis[N][N];
int ask[N],matched[N];
inline bool found(int x)//增广路
{
for (int i = 1 ; i <= m ; i++)
if (vis[x][i])
{
if (ask[i])
continue;
ask[i] = 1;
if (!matched[i] || found(matched[i]))
{
matched[i] = x ;
return true;
}
}
return false;
}
inline void match()
{
int cnt = 0;//cnt是计数器
memset(matched,0,sizeof(matched));
for (int i=1;i<=n;i++)
{
memset(ask,0,sizeof(ask));
if (found(i))
cnt++;//找到
}
ans = cnt;
}
int main()
{
scanf("%d%d%d",&n,&m,&e);
for (int i=1;i<=e;i++)
{
int x,y;
scanf("%d%d",&x,&y);
vis[x][y] = 1;//标记路径
}
match();//匈牙利算法
printf("%d \n",ans);
return 0;
}