二分图学习笔记

二分图算是一个比较简单的内容了吧。。
主要是建模,这个以后复习网络流再深究了。
oiwiki二分图
粘点板子跑路了。

一些性质

转自Little_Fall

\(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];
        }
    }
}
posted @ 2021-05-11 15:12  Isenthalpic  阅读(48)  评论(0编辑  收藏  举报