zoukankan      html  css  js  c++  java
  • 二分图学习笔记

    二分图算是一个比较简单的内容了吧。。
    主要是建模,这个以后复习网络流再深究了。
    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];
            }
        }
    }
    
  • 相关阅读:
    MySQL用户管理
    linux下杀死进程(kill)的N种方法
    Windows查看某个端口被谁占用
    SQL语句
    CentOS6.5 安装mysql-5.7.9
    Linux服务器安全之用户密钥认证登录
    Linux添加/删除用户和用户组
    linux命令killall 、kill 、pkill 命令详解
    linux下cat命令详解
    linux之sed用法
  • 原文地址:https://www.cnblogs.com/Xxhdjr/p/14755498.html
Copyright © 2011-2022 走看看