二分图(超详细!!!)

news/2024/11/22 23:44:58/

0x00 二分图

二分图是一个图,它的顶点可以分为两个独立的集合uv,这样每一条边(u,v)要么从uv连接一个顶点,要么从vu连接一个顶点。换句话说,对于每一条边(u,v),要么u属于u,要么v属于v,或者u属于vv属于u,也可以说没有边连接同一集合的不同点。

0x01 二分图的判定

可以通过染色法判定二分图。如果可以通过两种颜色将图染色,使得相同集合中的顶点使用相同的颜色,则为二分图。请注意,仅可以使用两种颜色为偶数顶点个数的图染色。例如,请参见下图。

无法使用两种颜色为奇数顶点个数的图染色。

二分图的判定算法主要通过bfsdfs。首先通过bfs来确定给定的图是否是二分图。

  • 初始点染成红色(放置到集合U中)。
  • 用蓝色给所有邻居涂上颜色(放入第V组)。
  • 用红色给所有邻居的邻居涂上颜色(放入U组)。
  • 这样,将颜色分配给所有顶点,使其满足m路着色问题中m=2的所有约束。
  • 分配颜色时,如果发现一个邻居的颜色与当前顶点的颜色相同,则该图不是二分图。
class Graph():  def __init__(self, V):  self.V = V  self.graph = [[0]*V for _ in range(V)]  self.colorArr = [-1]*self.V def isBipartiteUtil(self, src):  queue = []  queue.append(src)  while queue:  u = queue.pop()  if self.graph[u][u] == 1:  return Falsefor v in range(self.V):   if self.graph[u][v] == 1 and self.colorArr[v] == -1:  self.colorArr[v] = 1 - self.colorArr[u]  queue.append(v)  elif self.graph[u][v] == 1 and self.colorArr[v] == self.colorArr[u]:  return Falsereturn Truedef isBipartite(self): self.colorArr = [-1]*self.V for i in range(self.V): if self.colorArr[i] == -1: if not self.isBipartiteUtil(i): return Falsereturn Trueg = Graph(4)  
g.graph = [[0, 1, 0, 1],  [1, 0, 1, 0],  [0, 1, 0, 1],  [1, 0, 1, 0]]  print "Yes" if g.isBipartite() else "No"

也可以使用dfs来做。

V = 4 
def colorGraph(G, color, pos, c):  color[pos] = c  for i in range(0, V):  if G[pos][i]:  if color[i] == -1:  if not colorGraph(G, color, i, 1-c):return Falseelif color[i] == c:  return False return True def isBipartite(G):  self.colorArr = [-1]*self.V for i in range(self.V): if self.colorArr[i] == -1: if not colorGraph(G, color, pos, 1): return Falsereturn Trueif __name__ == "__main__":  G = [[0, 1, 0, 1],  [1, 0, 1, 0],  [0, 1, 0, 1],  [1, 0, 1, 0]]  if isBipartite(G): print("Yes")  else: print("No")  

上述代码中的图的表示是通过邻接矩阵存储的,当然可以通过数组模拟邻接表来处理。关于数组模拟邻接表的知识数组模拟邻接表(超详细!!!)

int n;      // n表示点数
int h[N], e[M], ne[M], idx;     // 邻接表存储图
int color[N];       // 表示每个点的颜色,-1表示为染色,0表示白色,1表示黑色// 参数:u表示当前节点,father表示当前节点的父节点(防止向树根遍历),c表示当前点的颜色
bool dfs(int u, int father, int c)
{color[u] = c;for (int i = h[u]; ~i; i = ne[i]){int j = e[i];if (color[j] == -1){if (!dfs(j, u, !c)) return false;}else if (color[j] == c) return false;}return true;
}bool check()
{memset(color, -1, sizeof color);bool flag = true;for (int i = 0; i < n; i ++ ){if (color[i] == -1){if (!dfs(i, -1, 0)) return false;}}return true;
}

0x02 二分图的最大匹配

二分图中的匹配是一组边的选择方式,使得一个端点不会对应两条边。最大匹配是最大边数的匹配。在最大匹配中,如果添加了任何边,则不再是匹配。给定的二部图可以有多个最大匹配。

这有什么用?

现实世界中有许多问题可以作为二分匹配来解决。例如,考虑以下问题:

m个职位申请者和n个职位。每个申请人都有他/她感兴趣的工作子集。每个职位空缺只能接受一个应聘者,一个职位应聘者只能被指定一个职位。找一份工作分配给申请者,以便尽可能多的申请者得到工作。

二分图的最大匹配问题实际上可以转化为最大流问题。

对于二分图来说,每条边的容量相当于01,所以我们可以将算法简化。任取一个匹配 M M M(可以使用空集或一条边),令 S S S是非饱和点(尚未匹配的点)的集合。

  • 如果 S S S是空集,则 M M M已经是最大匹配了。
  • S S S中取出一个非饱和点 u 0 u_0 u0作为起点,从此起点走交错路(交替属于 M M M和非 M M M的边构成的极大无重复点通路或回路) P P P
  • 如果 P P P是一个增广路径( P P P的终点也是非饱和点),则令 M = ( M − P ) ∪ ( P − M ) M=(M-P)\cup (P-M) M=(MP)(PM)
  • 如果 P P P不是增广路径,则从 S S S中去掉 u 0 u_0 u0,然后继续递归。

首先解释一下增广路径,这里的增广路径和最大流中有一些区别。从一个未匹配点出发,走交替路,如果途径另一个未匹配点(出发的点不算),则这条交替路称为增广路径。例如,左图的一条增广路如右图所示(图中的匹配点均用红色标出):

增广路径有一个重要特点:非匹配边比匹配边多一条。因此,研究增广路径的意义是改进匹配。只要把增广路径中的匹配边和非匹配边交换即可。由于中间的匹配节点不存在其他相连的匹配边,所以这样做不会破坏匹配的性质。交换后,图中的匹配边数目比原来多了1条。

int n;      // n表示点数
int h[N], e[M], ne[M], idx;     // 邻接表存储所有边
int match[N];       // 存储每个点当前匹配的点
bool st[N];     // 表示每个点是否已经被遍历过bool find(int x) //判断增广路径
{for (int i = h[x]; ~i; i = ne[i]){int j = e[i];if (!st[j]){st[j] = true;if (match[j] == 0 || find(match[j])){match[j] = x;return true;}}}return false;
}// 求最大匹配数
int res = 0;
for (int i = 0; i < n; i ++ )
{memset(st, false, sizeof st);if (find(i)) res ++ ;
}

上面这个算法就是匈牙利算法,通过dfs判断增广路径和之前最大流问题中的bfs判断最大路径区别。

reference:

https://www.geeksforgeeks.org/bipartite-graph/

https://www.acwing.com/blog/content/405/

https://www.geeksforgeeks.org/maximum-bipartite-matching/

https://www.cs.cmu.edu/~ckingsf/bioinfo-lectures/matching.pdf

https://blog.csdn.net/qq_40061421/article/details/82751020


http://www.ppmy.cn/news/853001.html

相关文章

二分图整理

定义&#xff1a;如果一张图的n个节点可以分成A,B两个不相交的非空集合&#xff0c;并且同一集合内的点之间没有边相连&#xff0c;那么称该无向图为二分图。 定理&#xff1a;二分图中不存在长度为奇数的环&#xff1b;就比如这样的就不是二分图 这一发现无论怎么分&#xff…

二 分 图

什么是二分图: 简单来说&#xff0c;如果图中点可以被分为两组&#xff0c;并且使得所有边都跨越组的边界&#xff0c;则这就是一个二分图。准确地说&#xff1a;把一个图的顶点划分为两个不相交子集 &#xff0c;使得每一条边都分别连接两个集合中的顶点。如果存在这样的划分…

二分图(仅二分图基础讲解)

闲的无聊来写个二分图的讲解吧。 读者可能会疑惑&#xff0c;二分图&#xff1f;什么叫做二分图&#xff0c;是由二分思想演变的图吗&#xff1f; 但是&#xff0c;二分图各位都见过&#xff0c;只是不知道它叫二分图。以下开始二分图的讲解 二分图&#xff1a; 大家小学肯…

embedding和向量数据库(pinecone)

embedding和向量数据库(pinecone) 玩了这么久的gpt&#xff0c;大家多少都会发现使用过程中有一些尴尬的点&#xff1a; LLM的训练数据是有ddl的&#xff0c;无法获取到最新的一些信息 LLM不知道答案&#xff0c;开始放飞自我&#xff0c;出现hallucination 想应用化&#xff…

Linux rpm命令详解

rpm -aq|grep yum|xargs rpm -e --nodeps #卸载所有yum相关包 rpm常见命令参数 用法: rpm [选项...] -a&#xff1a;查询所有套件&#xff1b; -b<完成阶段><套件档>或-t <完成阶段><套件档>&#xff1a;设置包装套件的完成阶段&#xff0c;并…

苹果开发者_苹果iOS14.2/iPadOS开发者预览版下载-苹果iOS14.2/iPadOS开发者预览版Beta4固件大全下载 v1.0...

苹果开发者预览版是一款可以帮助你更新最新的苹果系统的软件&#xff0c;很多人用习惯了苹果系统感觉改不过来了&#xff0c;其实习惯这个事情&#xff0c;突然的转变对于很多人来说都会有点不习惯&#xff0c;在这里你可以自己更新一个苹果的最新系统&#xff0c;虽然这个系统…

Mac 防还原系统(设置固件密码)

分享一个给Mac设备设置固件密码的方法&#xff0c;一起来看看具体步骤吧。 1.OS X用户可以在开机的时候按住Option键&#xff0c;进入Recovery HD。注意&#xff0c;Recovery HD是OS X系统自带的急救模式&#xff0c;主要用于重装系统与修复磁盘等操作&#xff0c;删除了会有…

简单提取iOS13的ipsw固件的内置壁纸(或文件)

1.先百度下“TransMac”,下载并安装。 没看到官方网站&#xff0c;就点第一个下载就好了 2.在下载安装的同时&#xff0c;将下载的ipsw文件的后缀ipsw改成rar,然后进行解压&#xff0c;得到下面文件夹里的东西。 其中有3个后缀为dmg的文件&#xff0c;找到大小最大的那个&#…