20240718二分图
一.基础概念
1.定义:
如果一个图的所有顶点可以被分成两个集合U和V,使得每条边连接的两个顶点都分别属于两个不同的集合,那么这个图就是一个二分图(Bipartite Graph)。
2.性质:
- 每个偶环都是二分图
- 如果一个二分图中存在奇环,则它不是二分图。
二.霍尔定理
前言:在hloj上有这个内容,不知道是干嘛的,但是反正就是学了一下,觉得用处还挺大
附带一个将Hall定理的好博客:https://www.cnblogs.com/wenyutao1/p/17784455.html
1.内容:
对于一个二分图,不妨假设左部节点个数n小于等于右部节点个数m,那么这个二分
图存在完美匹配(也就是匹配数为n)当且仅当我们从左部选出任意k(1≤k≤𝑛)
个点,它们连向的右部点的数量不小于k。
2.推广:
对于一个左部点有n个,右部点m个,最大匹配大于等于n-a凼且仅当从左部分选任意个k个点(1≤k≤n)连向右部点的数量≥k-a
同时也相当于最大匹配=\(|S|-max_{S^{`}\subseteq S}(|S^{`}|-|tr(S^{`})|)\)
\(tr(S^{`})\)是与\(S^{`}\)相邻的点集
三.如何判定
1.染色法:
算法原理就是,用黑与白这两种颜色对图中点染色(相当于给点归属一个集合),一个点显然不能同时具有两种颜色,若有,此图就不是二分图
染色法判定二分图
bool dfs(int u,int c)
{
color[u]=c;//当前点先染色
for(int i=h[u];~i;i=ne[i])
{
int j=e[i];//对于这个点连接的所有的点
if(color[j])//如果已经被染过色了
{
if(color[j]==c)
return false;
//就需要判断一下,如果两点颜色一样,染色就冲突了
}
else
if(!dfs(j,3-c))
return false;
//否则dfs去染下一个结点,赋予的颜色肯定要跟 c 不一样
//3 - 1 == 2,3 - 2 == 1
//同时传回染色成功与否的信息
}
return true;
}
bool check()
{
memset(color,0,sizeof color);//0 —— 未染色,1 —— 黑色,2 —— 白色
for(int i=1;i<=n;i++)
if(color[i]==0)//一旦某个点没染过色,dfs去染色
if(!dfs(i,1))
return false;//如果传回false显然失败,此图不是二分图
return true;
//否则true
}
所谓 最大匹配数 的意思就是:
两个集合分别选一个点,这两个点之间有边就确认一段关系(一个集合中的两点 占有 另一集合中同一个点 是不合法的 一夫一妻(确信) ),最多的关系数量就是这张二分图的最大匹配
匈牙利算法求二分图匹配
bool find(int x)//标准匈牙利
{
for(int j=1;j<=n;j++)
if(!st[j] and g[x][j])//x 点连向的所有点(因为是二分图,所以这些点都在右集合),如果存在边且没标记过
{
st[j]=true;
//标记一下,防止多次遍历
int t=match[j];
//右集合中该点的匹配对象
if(!t or find(t)) //没对象就可以和 x 匹配,有的话就让 t 尝试更改对象,能更改就和 x 匹配,不能就false
{
match[j]=x;
return true;
}
}
return false;
}
int main()
{
cin>>n;
int ans=0;
for(int i=1;i<=n;i++)//遍历左集合
{
memset(st,0,sizeof st);//每次都要重置标记
if(find(i))
ans++;//一旦有一个匹配,数量就++
}
cout<<ans;
return 0;
}
但匈牙利算法还是很优秀的,大部分情况时间都比较小
如果想要更优秀的算法左转 网络流 吧,匈牙利匹配本质上还是网络流的一种特殊形式,网络流可以更好地解决此类问题。