简介
judge from name
:可以一分为二的图就是二分图
judge from math
:一类特殊的图论模型,图中的每个节点分属与两个不同的点集 A,B
,每一条边所连的两个节点,必然一个在 集合 A
中,一个在集合B
中
二分图又称作二部图,是图论中的一种特殊模型。 设G=(V,E)是一个无向图,如果顶点V可分割为两个互不相交的子集,并且图中的每条边所关联的两个顶点分别属于这两个不同的顶点集,则称图G为一个
二分图
。
- 匹配:在图论中,一个「匹配」(matching)是一个边的集合,其中任意两条边都没有公共顶点。(是子图哦!)
- 最大匹配:具有最多边的匹配
- 完美匹配:所有的点都在匹配内
- 增广路:起始点和终点都为未匹配的点的交错轨
一、判断二分图
1.染色法(黑白染色法)
解释:由于二分图的点仅有
两个集合,所以定义两个集合中的其中之一为白点集,另一个为黑点集
对图中的所有点dfs,起始点定为黑色,由于二分图的性质,每一边的两点必然颜色不同
vector<int>g[M];
int color[M];// 每个点的颜色集合,-1表示颜色待定,1表示黑色,0表示白色
bool bipartite_judge(int x) //基于dfs的算法,x表示起始点
{
int v;
for(int i=0;i<g[x].size();i++)
{
v=g[x][i];
if(color[x]==color[v])return false; //一条边连接了2个颜色相同的点,证伪
if(color[v]==-1)
{
color [v]=!color [x];
if(!bipartite_judge(v))//回溯出现证伪,全部结束
return false;
}
}
return true;//流程走下来,无问题
}
2.奇环判定法
思路:如果一个图里有环,且环的节点数为>2
的奇数,由定义易知不可能实现二分了(其实染色法已经自带奇环判定咯)
二、二分图的最大匹配
- 介绍:
匹配
匹配:在图论中,一个「匹配」(matching)是一个边的集合,其中任意两条边都没有公共顶点。
1.匈牙利算法
效果:寻找最大匹配数(最大匹配的边数)
手段:递归的寻找所有的单点匹配
核心:类似于搜索回溯的思想
实现:
bool vis[M]; 判断一个点有被判断出现 (每次对单点匹配的时候都要初始化)
int match[M]; 记录二分图一边点的匹配对象 (持久化且不发生改变)
每次对vis
数组进行对于单点进行修改所以
bool find(int x)
{
for(int i=h[x];~i;i=nxt[i])
{
int v=to[i];
if(vis[v])
{
vis[v]=true;
if(match[v]=0 || find(match[v])) //增广路的寻找!!
{
match[v]=x;
return true;
}
}
}
return false;
}
完全的代码贴在这里!!:
#include<bits/stdc++.h>
using namespace std;
const int M=502;
int cnt;
int n1,n2,m,a,b,ans;
int h[M],to[M],nxt[M];
bool vis[M];
int match[M];
void add(int x,int y)
{
nxt[++cnt]=h[x];
h[x]=cnt;
to[cnt]=y;
}
bool find(int x)
{
int v;
for(int i=h[x];~i;i=nxt[i])
{
v=to[i];
if(!vis[v])
{
vis[v]=true;
if(match[v]==0||find(match[v]))
{
match[v]=x;
return true;
}
}
}
return false;/// 经过一套判断,已经对二分图具有了判断,前期没有return
}
int main()
{
cin>>n1>>n2>>m;
memset(h,-1,sizeof h);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&a,&b);
add(a,b);
}
for(int i=1;i<=n1;i++)
{
memset(vis,0,sizeof vis);
if(find(i))ans++;
}
cout<<ans;
}