定义:
一组点集可以分为两部分,且每部分内各点互不相连,两部分的点之间可以有边。
判定:
所有回路的长度都是偶数,即不存在长度为奇数的环。
二分图最大匹配算法:匈牙利算法,时间复杂度,最坏 O(点数 × 边数)
//二分图匹配
//n为男生数目,m为女生数目
//line[i][j]第i个男生和第j个女生有关系
//g[i]第i个女生的对象
//注意used每次都要清零
int n,m,t;
int line[N][N],g[N],used[N];
bool findd(int x)
{
for (int j=1;j<=m;j++)
{
if (line[x][j] && !used[j])
{
used[j]=1;
if (g[j]==0 || findd(g[j]))
{
g[j]=x;
return true;
}
}
}
return false;
}
int main()
{
memset(g,0,sizeof g);
memset(line,0,sizeof line);
int u=0;
for (int i=1;i<=n;i++)
{
memset(used,0,sizeof used);
if (findd(i)) u++;
}
cout<<u;
return 0;
}
变种1: 最小点覆盖
定义:在二分图中求最少的点,让每条边都至少和其中的一个点关联,这就是二分图中的最小顶点覆盖。
结论:最小点覆盖 = 最大匹配
方案:
证明:
证明来自:http://www.matrix67.com/blog/archives/116
代码:
//a,b 为点覆盖的方案
int v[N]={0},q[N]={0},qq[N]={0};
void fang_an(int x)
{
q[x]=1;
for (int j=1;j<=m;j++)
{
if (line[x][j] && !qq[j] && g[j] && g[j]!=x)
{
qq[j]=1;
fang_an(g[j]);
}
}
}
for (int i=1;i<=m;i++) if (g[i]) v[g[i]]=1;
for (int i=1;i<=n;i++) if (!v[i] && !q[i])fang_an(i);
for (int i=1;i<=n;i++) if (!q[i]) a.push_back(i);
for (int i=1;i<=m;i++) if(qq[i]) b.push_back(i);
变种2:DAG图的最小路径覆盖
定义:用尽量少的不相交简单路径覆盖有向无环图(DAG)G的所有顶点,这就是DAG图的最小路径覆盖问题。换句话说,就是选尽量少的边,报这些边都从起点走到终点,恰好能经过整个图的所有顶点,问你这些边的数量。
结论:DAG图的最小路径覆盖数=节点数(n)-最大匹配数(m)
变种3:二分图的最大点独立集
定义:实质是个点集,这个集合里的点无论怎样都两两相连不到一起,满足这个要求的点数最大的一个。
结论:最大独立集数=节点数(n)-最大匹配数(m)
二分图的最佳完美匹配
KM算法
最大顶标和等于最小权匹配
最小顶标和等于最大权匹配
// 要求最小权时取负
int numX, numY; // 两侧结点数
int g[N+2][N+2], lx[N+2], ly[N+2], slack[N+2]; // 类型与权值保持一致
int my[N+2], visx[N+2], visy[N+2];
int Dfs(int x){
visx[x]=1;
for (int y=1; y<=numY; y++) if (!visy[y]){
int temp=lx[x]+ly[y]-g[x][y];
if (!temp){
visy[y]=1;
if (!my[y] || Dfs(my[y])){
my[y]=x;
return 1;
}
} else
slack[y]=min(slack[y], temp);
}
return 0;
}
void KM(){
memset(my, 0, sizeof(my));
memset(lx, 0x80, sizeof(lx));
memset(ly, 0, sizeof(ly));
for (int i=1; i<=numX; i++)
for (int j=0; j<=numY; j++)
lx[i]=max(lx[i], g[i][j]);
for (int x=1; x<=numX; x++){
memset(slack, 0x7f, sizeof(slack));
while (1){
memset(visx, 0, sizeof(visx));
memset(visy, 0, sizeof(visy));
if (Dfs(x)) break;
int Min=0x7fffffff;
for (int i=1; i<=numY; i++) if (!visy[i])
Min=min(Min, slack[i]);
for (int i=1; i<=numX; i++) if (visx[i])
lx[i]-=Min;
for (int i=1; i<=numY; i++)
if (visy[i]) ly[i]+=Min;
else slack[i]-=Min;
}
}
int ans=0;
for (int i=1;i<=numY;i++) ans+=g[my[i]][i];
//或者
for (int i=1;i<=numX;i++) ans+=lx[i]+ly[i];
return ans;
}