二分图算是一个比较简单的内容了吧。。
主要是建模,这个以后复习网络流再深究了。
oiwiki二分图
粘点板子跑路了。
一些性质
\(1\). 最小点覆盖集:点覆盖集是指图中的一个点集,且图中所有的边都至少有一个端点在点集中。最小点覆盖集即为点数最小的点覆盖集。
\(2\). 最少边覆盖集:边覆盖集是指图中的一个边集,且图中所有的点都至少为边集中一条边的端点。最小边覆盖集即为边数最少的边覆盖集。
\(3\). 最大独立集:独立集是指图中的一个点集,且这些点之间两两无边相连。最大独立集是指点数最多的独立集。
\(4\). 最大团:团是指图中的一个点集,且这些点之间两两有边相连(完全图)。最大团是指点数最多的团。
公式:
\(1\). 二分图的最小点覆盖 = 二分图的最大匹配(证明见König定理)
\(2\). 二分图的最少边覆盖 = 点数 - 二分图的最大匹配
\(3\). 二分图的最大独立集 = 点数 - 二分图的最大匹配
\(4\). 无向图的最大团 = 无向图补图的最大独立集
最小路径点覆盖
问题:给定一个DAG(有向无环图),用尽量少的不相交的简单路径,覆盖所有点(每个点只被覆盖一次)。
做法:将每个点拆成\(u\)和\(u'\),对于每条有向边\((x,y)\),在新图中使\(x\)向\(y'\)连边。
所得新图显然是二分图,答案即为点数\(n\)减去新图的最大匹配数。
证明:
特别的是,如果允许点重复经过,我们对原邻接矩阵做一次传递闭包,再对新的邻接矩阵执行上述过程。
二分图染色
当且仅当不存在奇环
bool dfs(int u){
for(int i=now[u];i;i=pre[i]){
int v=to[i];
if(col[u]==col[v])return false;
col[v]=3-col[u];
if(!dfs(v))return false;
}
return true;
}
匈牙利算法
\(O(NM)\),网络流用\(dinic\)只要\(O(M \sqrt N)\)。
bool dfs(int u){
for(int i=head[u];i;i=nxt[i]){
int v=to[i];
if(vis[v]==tag)continue;
vis[v]=tag;
if(!path[v]||dfs(path[v])){
path[v]=u;return 1;
}
}
return 0;
}
for(int j=1;j<=n;j++){
tag++;
if(dfs(i))ans++;
}
带权二分图完美匹配 KM算法
\(lyd\)算阶上是个\(O(N^4)\)的,事实上从交错树的根节点直接遍历是很浪费的,所以我们每次从新加的边开始\(dfs\),那么就可以做到\(O(n^3)\).
bool dfs(int u,int fa){
va[u]=true;
for(int v=1;v<=n;v++)
if(!vb[v])
if(la[u]+lb[v]-w[u][v]==0){
vb[v]=true;
lst[v]=fa;
if(!path[v]||dfs(path[v],v)){
path[v]=u;
return true;
}
}else if(upd[v]>la[u]+lb[v]-w[u][v]){
upd[v]=la[u]+lb[v]-w[u][v];
lst[v]=fa;
}
return false;
}
void KM(){
for(int i=1;i<=n;i++){
la[i]=-INF;lb[i]=0;
for(int j=1;j<=n;j++)
la[i]=max(la[i],w[i][j]);
}
for(int i=1;i<=n;i++){
for(int j=0;j<=n;j++)
upd[j]=INF,va[j]=vb[j]=0;
int st=0;
path[0]=i;
while(path[st]){
int dt=INF;
if(dfs(path[st],st))break;
for(int j=1;j<=n;j++)
if(!vb[j]&&dt>upd[j]){
dt=upd[j];st=j;
}
for(int j=1;j<=n;j++){
if(va[j])la[j]-=dt;
if(vb[j])lb[j]+=dt;
else upd[j]-=dt;
}
vb[st]=true;
}
while(st){
path[st]=path[lst[st]];
st=lst[st];
}
}
}