【学习笔记】二分图

二分图

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

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

判定

直接 \(dfs\) 染色

inline void dfs(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],dfs(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;
}

最小点覆盖

在二分图中的一个点集,使得图中所有边都存在至少一个端点属于这个点集

\(\rm König\) 定理:

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

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

  1. 求出最大匹配。

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

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

证明

经过上述构造方法后

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

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

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

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

所以数量值相等



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

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

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

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

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

最大独立集

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

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

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

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

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

例题:骑士放置

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

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

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

同时我们发现骑士所在的位置和可以攻击到的位置的 横纵坐标之和 是不一样的

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


最小路径覆盖问题

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

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

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

证明:

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

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

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

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

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

例题如下

似乎是模板题

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

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

那么问题又转化成了最小点覆盖(\(x\rightarrow z\)\(u \rightarrow y \rightarrow 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\),就连边

posted @ 2020-04-05 16:58  yspm  阅读(160)  评论(0编辑  收藏  举报