二分图定义:
二分图又称作二部图。设G=(V,E)是一个无向图,如果顶点V可分割为两个互不相交的子集(A,B),并且图中的每条边(i,j)所关联的两个顶点i和j分别属于这两个不同的顶点集,则称图G为二分图。
二分图判定:
定理:
一张无向图是二分图,当且仅当图中不存在奇环。
可用染色法来判断奇环是否存在。
bool flag=0;
void dfs(int x,int color)
{
if(flag)
return ;
v[x]=color;
int y;
for(int i=head[x];i;i=Edge[i].nxt)//前向星存图
{
y=Edge[i].to;
if(v[y]==0)
dfs(y,3-color);
else if(v[y]==color)
{
flag=1; //存在奇环 不是二分图
return ;
}
}
}
for(int i=1;i<=n;i++)
if(!v[i])
dfs(i,1);
参考一:
https://blog.csdn.net/pi9nc/article/details/11848327
匈牙利算法:(求无边权二分图的最大匹配)
算法思想:匈牙利算法其实是在保证匹配不减少的情况下,试图改变以前的某些策略,让新加入的节点被匹配上。
dfs版本:(每个左侧点都要调用一次dfs(那么use数组是针对右侧点的) 若返回为true 说明当前点可以被匹配上)
bool dfs(int x)
{
for (int i = 1; i <= sizey; i++)
{
if (use[i] == 0 && a[x][i])
{
use[i] = 1;
if (pre[i] == 0 || dfs(pre[i]))
{
pre[i] = x;
return true;
}
}
}
return false;
}
参考二:
https://blog.csdn.net/chenshibo17/article/details/79933191
KM算法:(带权二分图的最大权完美匹配)
bool findpath(int x)
{
int tempDelta;
visx[x]=true;
for(int y=0;y<ny;++y)
{
if(visy[y])
continue;
tempDelta=lx[x]+ly[y]-G[x][y];
if(tempDelta==0)
{//(x,y)在相等子图中
visy[y] = true;
if(match[y] == -1 || findpath(match[y]))
{
match[y]=x;
return true;
}
}
else if(slack[y]>tempDelta)
slack[y]=tempDelta;
//(x,y)不在相等子图中且y不在交错树中
}
return false;
}
int KM()
{
memset(match,-1,sizeof(match));
for(int x=0;x<nx;++x)
{
memset(slack,INF,4*ny);
while(true)
{
memset(visx,0,sizeof(visx));
memset(visy,0,sizeof(visy));
if(findpath(x))
break;
else
{
int delta=INF;
for(int j=0;j<ny;++j)
if(!visy[j]&&delta>slack[j])
delta=slack[j];
for(int i=0;i<nx;++i)
if(visx[i])
lx[i]-=delta;
for(int j=0;j<ny;++j)
{
if(visy[j])
ly[j]+=delta;
else
slack[j]-=delta;
}
}
}
}
int ans=0;
for(int i=0;i<ny;i++)
ans+=G[match[i]][i];
return ans;
}
补充:
最大匹配数:最大匹配的匹配边的数目
最小点覆盖数:选取最少的点,使任意一条边至少有一个端点被选择
最大独立数:选取最多的点,使任意所选两点均不相连
最小路径覆盖数:对于一个 DAG(有向无环图),选取最少条简单路径,使得每个顶点属于且仅属于一条路径。路径长可以为 0(即单个点)。
最大匹配边数 = 最小点覆盖数
最大独立数 = 顶点数 - 最大匹配数
最小路径覆盖数 = 顶点数 - 原DAG图的拆点二分图的最大匹配数