二分图 学习笔记
来源:互联网 发布:gitlab ci.yml java 编辑:程序博客网 时间:2024/05/18 16:56
很久之前就学过二分图,但是感觉当时理解的并不好。今天重新复习了一下二分图——for noip,在此写下一些新的体会。
二分图的定义
摘自ATP的blog——
二分图顾名思义就是可以分成两部分的图。并且这两部分内部不能有边相连。形式化地,定义图
G={V,E} ,A 是G 的一个子集。如果对于∀(x,y)∈E ,都有(x∈A)∧(y∈S−A) 或者(y∈A)∧(x∈S−A) (总之x 和y 不能同时属于A 或S−A ),那么称G 是一个二分图。
由于二分图的特殊性,我们可以用它来搞很多事情= =
基本概念
二分图上乱七八糟的东西非常多,比如说——
匹配:在图论中,一个匹配是一个边的集合,其中任意两条边都没有公共顶点。
我们定义匹配点、匹配边、未匹配点、非匹配边,它们的含义非常显然。
最大匹配:一个图所有匹配中,所含匹配边数最多的匹配,称为这个图的最大匹配。
完美匹配:如果一个图的某个匹配中,所有的顶点都是匹配点,那么它就是一个完美匹配。显然,完美匹配一定是最大匹配(完美匹配的任何一个点都已经匹配,添加一条新的匹配边一定会与已有的匹配边冲突)。但并非每个图都存在完美匹配。
交替路:从一个未匹配点出发,依次经过非匹配边、匹配边、非匹配边…形成的路径叫交替路。
增广路:从一个未匹配点出发,走交替路,如果途径另一个未匹配点(出发的点不算),则这条交替路称为增广路。
二分图的判断
由二分图的概念我们可以得到一个非常显然的判断方法。将一个图黑白染色,要求有边直接相连的点被染成不同的颜色,如果无法染色或发生冲突,则不是二分图。
正确性显然啊▽
二分图的相关算法和结论
以下所有的结论都没有非常严谨的证明,全部是我自己yy的,可能会有一些不充分或不合理的地方叭,见笑。
最大匹配
最大匹配的概念之前已经介绍过了,那么如何求最大匹配呢?
强烈建议看这篇文章:
趣写算法系列之——匈牙利算法
不过我不是很推荐用这个blog里的代码,它每次都memset会很慢。其实vis数组可以不用bool,而是打一个标记,这样可以节省时间。
贴一个二分图判断+最大匹配的模板
#include<iostream>#include<cstring>#include<cstdio>using namespace std;#define N 40005int n,m,x,y,ans;bool flag;int tot,point[N],nxt[N*2],v[N*2];int belong[N],vis[N],col[N];void clear(){ ans=0; tot=0;memset(point,0,sizeof(point));memset(v,0,sizeof(v));memset(nxt,0,sizeof(nxt)); memset(belong,0,sizeof(belong));memset(vis,0,sizeof(vis));memset(col,-1,sizeof(col));}void add(int x,int y){ ++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;}void dfs(int x,int fa){ for (int i=point[x];i&&flag;i=nxt[i]) if (v[i]!=fa) { if (col[v[i]]==-1) { col[v[i]]=col[x]^1; dfs(v[i],x); } else { if (col[v[i]]!=(col[x]^1)) flag=false; return; } }}bool find(int x,int k){ for (int i=point[x];i;i=nxt[i]) if (vis[v[i]]!=k) { vis[v[i]]=k; if (!belong[v[i]]||find(belong[v[i]],k)) { belong[v[i]]=x; return true; } } return false;}int main(){ while (~scanf("%d%d",&n,&m)) { clear(); if (n==1) { puts("No"); continue; } for (int i=1;i<=m;++i) { scanf("%d%d",&x,&y); add(x,y); } flag=true; col[1]=0;dfs(1,0); if (!flag) { puts("No"); continue; } for (int i=1;i<=n;++i) if (find(i,i)) ans++; printf("%d\n",ans); }}
个人的一点点理解:
实际上匈牙利算法的每一次增广只是在不断地调整匹配边而已,直到不能匹配为止。
你会发现,最大匹配非常好用。二分图的很多问题都能转化为这个基本模型。
最小路径覆盖
一个有向无环图(不一定是二分图),要求用尽量少的不相交的简单路径覆盖所有的节点。
建图方法:将原图每个点拆为两个点,对于每一条边
一个结论:最小路径覆盖=顶点数-最大匹配
注意上面的顶点数是指原图的顶点数。
证明:
假设每一个点的连出点成为出点,连入点成为入点。那么我们可以从每个点的入点向出点连一条有向边。这条边可以称之为辅助边,它的作用是将拆开的点重新联系起来。那么我们从每一个未访问过的点的入点出发,依次经过辅助边、匹配边、辅助边、匹配边直到无路可走为止,这就画出了一条一条的路径覆盖。可见,如果刚开始每一个点对答案的贡献为1的话,每多一个匹配答案就会减少1。就可以证明上面那个结论了。
最大独立集
寻找一个最大点集
二分图的最大独立集=顶点数-最大匹配
证明:
看到这个公式,我们可以形象地将其理解为先选出所有没有匹配的点,然后再在每一对匹配点对中选出一个点来。可以证明这样做是显然正确并且唯一成立的。
①因为两两无边,所以每一对匹配只能选一个点,并且必须是最大匹配。
②一定存在一种方案,使得从每一个匹配中选出一个点,使其和其它点没有边。可以用反证法。
1°假设存在两个匹配,从一个匹配中选一个点,不管怎样选,这两个点之间一定有边。那么可以发现这四个点是一个完全图而不是二分图,与原命题矛盾。
2°假设存在一个匹配,不管选哪个点,都和其它未匹配点有边。那么假设匹配边
综上,命题得证。
最大团
寻找一个最大点集,任意两点间有边。
二分图的最大团=其补图的最大独立集
补图就是把二分图中两集合相互有边的点全部变为无边,相互无边的点全部变为有边。显然二分图的补图还是二分图。
这个结论非常显然啊,图也是互补的条件也是互补的,所以一定是等价的啊= =
最小顶点覆盖
寻找一个最小点集
最小顶点覆盖=最大匹配
证明:
①充分性:已知一个最小顶点覆盖,推出最大匹配。
1°如果这个点集是最小顶点覆盖,那么对于点集中一定不存在
2°用反证法:假设这个匹配不是最大匹配,那么一定存在边
②必要性:已知一个最大匹配,推出最小顶点覆盖。
1°因为是最大匹配,那么未匹配边里一定不存在一条两个端点都未匹配的边,那么就从每一对匹配中选出一个点,一定存在一种方案使其是一个合法的顶点覆盖。
2°同样用反证法,如果这个点集不是最小的,那么一定存在一条边的两个端点都在最大匹配里,与已知矛盾。
至此,可以证明由最大匹配能推出最小顶点覆盖。
证毕。
感觉二分图的相关算法还有很多啊,比如什么稳定婚姻问题balabala,以后还要继续学,持续更新。
- 二分图学习笔记
- 二分图 学习笔记
- 二分图匹配学习笔记
- 二分图匹配学习笔记
- 二分图匹配学习笔记
- 学习笔记----二分图的最大匹配
- 【补习时间】二分图相关学习笔记
- 学习笔记-二分图匹配(匈牙利算法)
- hdu 2063(二分图最大匹配)学习笔记
- 二分图匹配学习笔记(网络流)
- 二分图复习笔记
- 二分图学习
- 二分图学习整理
- 二分图学习
- 二分图学习小结
- 二分图学习 - hiho1121
- 学习一个二分图
- Java二分查找算法学习笔记。
- 2016年11月10日学习总结
- 常见算法
- HDU2071_Max Num
- 优秀博客链接
- JQ中的hover、mousexx的区别
- 二分图 学习笔记
- JAVA数组
- 固定数字排除
- Nmap学习笔记(一)
- python设计的猜数字游戏--学习笔记4-编程
- 平面分割问题
- 近期学习状况
- HDU2092_整数解
- Visual studio 2012 快捷键