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

    二分图

    图上的点可以完美的被分成两部分,使得每一个部分的点中互相没有连边

    或者说:可以黑白染色、没有奇环

    判定

    直接 (dfs) 染色

    	inline void dfs1(int x){
    		for(int i=head[x];i;i=e[i].nxt) {
    			int t=e[i].to;
    			if(col[t]==-1) col[t]=1-col[x],dfs1(t);
    		}return ;
    	}
    

    这里的 (col[space]) 就是表示每个点所属于的点集标号

    我第一次写的是按照 (0,1) 这样走的,还可以有 (1,2) (就是在 (col) 计算的时候拿 (3) 减呗)

    最大匹配

    匹配:二分图的一个边集,使得边集中的所有边没有公共端点

    最大匹配:边集中 (size) 最大的一个

    匹配边:顾名思义,在匹配的边集中的边

    增广路(交错路):一个从连接左部非匹配点和右部非匹配点的路径

    使得匹配边和非匹配边交替出现,长度应为奇数

    正是增广路的这个定义,使得我们可以在找到一条增广路的时候把路径上的所有的边取反,然后边集大小会 (+1)

    求解主要是匈牙利算法

    就是每次dfs找增广路,如果找到了答案加 (1)

    	inline bool dfs(int x)
    	{
    		for(int i=head[x];i;i=e[i].nxt)
    		{
    			int t=e[i].to; if(vis[t]) continue;
    			vis[t]=1; if(to[t]==-1||dfs(to[t])) return to[t]=x,1;
     		}
    		return 0;
    	}
    

    最小点覆盖

    在二分图中的一个点集,使得图中所有边有端点在这个点集中(定义真的可懂)

    (König) 定理:

    二分图最小点覆盖集的大小 (=) 二分图最大匹配集的大小

    先说构造最小点覆盖的方法:

    1. 求出最大匹配。

    2. 从右部每个未匹配点出发寻找增广路(一定失败,要不然就不是最大匹配了),标记访问过的节点。

    3. 取左部标记点、右部未标记点,构成一组最小覆盖。

    画画图就可以理解了

    然后是证明:

    经过上述构造方法后

    右部未匹配点一定是标记点——因为它们是出发点

    左部未匹配点一定是未标记点——如果被标记则找到了增广路(两边都是未匹配点诶)

    一对匹配点都被标记或者都未标记——因为左部匹配点只能通过右部到达

    取左部标记点、右部未标记点,恰好使得每对匹配点被取走一个

    所以数量值相等


    匹配边一定被覆盖——每对匹配点取走一个

    不存在连接两个未匹配点的边——否则出现仅包含1条边的增广路

    连接左部匹配点和右部未匹配点的边——后者是出发点,前者一定被标记

    连接左部未匹配点和右部匹配点的边——后者未被标记,否则存在增广路

    所以所有的边都被覆盖了,大功告成

    最大独立集

    定义:任意两点在图中都没有边相连的点集称为图的独立集。

    定理:二分图最大独立集 (=) 图的点数 (-) 二分图最大匹配

    证明:选出最多的点构成独立集

    (Leftrightarrow) 在图中去掉最少的点,使剩下的点之间没有边。

    (Leftrightarrow) 用最少的点覆盖所有的边,去掉的是最小覆盖。

    挺好理解的吧

    例题:骑士放置

    给定一个(n imes m) 的矩形和几个禁止放置点,求最多可以放几个骑士

    首先看出来是求最大独立集不难吧

    建图就是对当前点和可以被攻击的点建图就行

    一个比较好的 (trick)

    我们发现骑士所在的位置和可以攻击到的位置的 (id) 是不一样的

    (这里的 (id) 是横纵坐标之和)

    这样就可以省略 (dfs) 染色的过程了,直接建图的时候看一下奇偶性就可以了

    注意: 这里的障碍点不要写(while(t--))

    原因是 (t) 在后面统计答案的时候还要被减掉,如果减没了就完蛋了(笔者卡了半小时)

    最小路径覆盖问题

    最小路径覆盖:用尽量少的不相交简单路径覆盖有向无环图的所有顶点。

    做法:把每个点拆成左边右边两个,对于每条边((u,v)),链接((l_u,r_v))

    跑最大匹配,最小路径覆盖数 = 原有向图节点数 – 新二分图最大匹配数

    证明:

    按照定义,我们选取的路径上的点都是入度、出度为(1)

    除了起点没有入度,终点没有出度

    而拆点就是因为匹配中每个点的连接的边不多于 (1)

    路径上的边对应匹配边,有向图中的出点对应左部点

    所以最小路径覆盖 (=) 最小未被匹配的点 (=n-) 最大匹配

    例题如下

    似乎是模板题

    首先要发现这个题的两个相交的路径:(x ightarrow y ightarrow z) 和路径 (u ightarrow y ightarrow v)

    这里如果我们添加一条边((x,z))

    那么问题又转化成了最小点覆盖((x ightarrow z)(u ightarrow y ightarrow v)

    这里搞一个 (Floyd) 传递闭包搞连通性然后最大匹配就好了

    例题

    (贪心一发,如果重叠少的做法肯定优)

    我们发现这个木板只有横竖两种摆放方式,而且如果都放在这里的话就重叠了

    所以我们让每行每列为点,每个行列的交点为边建图,然后是个最小点覆盖

    建图:如果遇到障碍点标号就加一,障碍点在这里的定义是好地

    	for(int i=1;i<=n;++i){
    		for(int j=1;j<=m;++j){
    			if(g[i][j]=='*') ++c1;
    			while(g[i][j]=='*') id1[i][j++]=c1;
    		}
    	}
    	for(int j=1;j<=m;++j){
    		for(int i=1;i<=n;++i){
    			if(g[i][j]=='*') ++c2;
    			while(g[i][j]=='*') id2[i++][j]=c2;
    		}
    	}
    
    • Tyvj1935 导弹防御塔

    首先我们这个最值并不好做,那么转二分答案

    二分时间 (T)(T) 是不是可行

    (check) 函数里面做二分图完备匹配

    预处理每一座塔和每个入侵者的距离

    塔和入侵者是可以分列左右的,所以这玩意是个二分图

    拆点:若某座塔在 (T) 秒内可以发射 (X) 次导弹,就把这座塔拆成 (X) 个点。

    建边的条件:

    导弹 (A)(S) 秒时发射,飞到入侵者 (B)(V) 秒,若 (S+V le T),就连边

  • 相关阅读:
    const,var,let区别(转载)
    在windows上搭建redis集群
    Linux学习笔记—vim程序编辑器
    Linux学习笔记—文件与文件系统的压缩与打包(转载)
    Linux学习笔记—Linux磁盘与文件系统管理(转载)
    五,mysql优化——sql语句优化小技巧
    八,mysql优化——读写分离
    六,mysql优化——小知识点
    七,mysql优化——表的垂直划分和水平划分
    三,mysql优化--sql语句优化之索引一
  • 原文地址:https://www.cnblogs.com/yspm/p/12637929.html
Copyright © 2011-2022 走看看