首页 > 其他分享 >二分图全面学习笔记

二分图全面学习笔记

时间:2024-10-11 21:13:15浏览次数:8  
标签:二分 ww 匹配 标记 int 笔记 学习 edges

二分图全面学习笔记

Part1:二分图的定义与判定方法

首先,我们要知道二分图的定义是什么。

二分图的定义

​ 如果一张无向图的 \(n\) 个节点可以分成 \(A,B\) 两个不相交的非空集合,并且同一个集合之中的两个点之间没有边相连接,那么称该无向图为二分图 (Bipartite Graph)

举个栗子

Snipaste_2024-08-29_19-45-50

很显然的,这个图是一个二分图

那么,二分图又有什么性质呢?

二分图的性质

  • 同一个集合之中的点之间没有边相连接
  • 图上没有奇环
    所谓奇环,就是长度为奇数的环。显然,由于一个集合内的点之间没有直接的边相连,所以从这个点出发之后一定要经过偶数条边才能回到这个集合。所以说,二分图上没有奇环。

所以,知道了这些,我们该如何判定二分图呢?

二分图的判定方法之:染色法

我们可以用染色法来判定二分图。

即我们用两种颜色来标记图上的点,当一个点 \(i\) 被标记后,用不同的颜色标记所有与之相邻的点。这是因为所有与一个点有直接连接的点都要在另一个集合之中,所以需要染上另一种颜色。

如果有一个与 \(i\) 相邻的点和 \(i\) 染的颜色相同,即说明在这个集合内有两个点有直接的边相连,那么说明这个图不是二分图。然后将这个过程递归的进行下去,直到所有节点都被染上颜色为止。

这个过程可以用 BFS 或者 DFS 实现

代码如下

点击查看代码
#include<bits/stdc++.h>
using namespace std;
struct edge
{
	int f,t;
};
edge edges[100000];
struct node
{
	int num;
	int col;
	vector<int> to;

};
node nodes[100000];
int n,m;
int tag=1;
bool continue_tag=1;//是否有两个相同节点染上了相同颜色 
void dfs(int x)//x 表示现在在哪个节点
{
	int col=nodes[x].col;
	int to;
	for(int yy=0; yy<nodes[x].to.size(); yy++)
	{
		if(continue_tag)//如果还要继续的话 
		{
			to=edges[nodes[x].to[yy]].t;
			if(nodes[to].col==0)//如果还没有染色过 
			{
				nodes[to].col=3-col;//染上相反颜色 
				dfs(to);//递归遍历 
			}
			else//被染色过 
			{
				if(nodes[to].col==col)//如果颜色相同 
				{
					continue_tag=0;//已经不是二分图了,所以不用再继续了 
					return;
				}
			}
		}
	}
}
int main()
{
	ios::sync_with_stdio(0);
	cin>>n>>m;
	int a,b;
	for(int yy=1; yy<=m; yy++)
	{
		cin>>a>>b;
		edges[yy].f=a;
		edges[yy].t=b;
		edges[yy+m].f=b;
		edges[yy+m].t=a;
		nodes[a].to.push_back(yy);
		nodes[b].to.push_back(yy+m);
	}
	for(int ww=1; ww<=n; ww++)//防止图不连通 
	{
		if(nodes[ww].col==0)
		{
			continue_tag=1;
			dfs(ww);
			if(continue_tag==0)
			{
				tag=0;
				break; 
			}

		}
	}
	if(tag==0)
	{
		cout<<"No";
	}
	else
	{
		cout<<"Yes";
	}
	return 0;
}

判定复杂度 \(O(n+m)\)

Part2:二分图最大匹配

设 G 为二分图,若 G 的子图 M 中,任意两条边都没有公共节点,那么称 M 为二分图 G 的一组匹配。 在二分图中,包含边数最多一组匹配称为二分图的最大匹配

说人话就是,设二分图的两个集合为 \(G_1,G_2\), 两个集合之中的点互相有连边,\(G_1\) 集合中点要和 \(G_2\) 中的 与自己连了边的 点 匹配,同时,与另一个点匹配了的点不能与别的点匹配,求最大的能匹配数

再通俗一点就是:比如说有一场宴会,男孩和女孩跳舞,并且他们必须互相喜欢才能一起跳舞,一个男孩可能喜欢 \(0\) 个或多个女孩,一个女孩也可能喜欢 \(0\) 个或多个男孩,但一个男孩和他喜欢地女孩跳舞之后就不能和其他他喜欢地女孩跳舞,女孩亦是如此。请问最多可以多少对男孩女孩一起跳舞

那么,二分图的最大匹配该怎么求呢?

匈牙利算法

有一种叫匈牙利算法的算法可以在 \(O(nm)\) 的时间复杂度之内来解决这个问题

我们依次枚举 \(G_1\) 中的点 \(i\),在 \(G_2\) 中寻找他们的匹配.
我们依次遍历 在 \(G_2\) 中与 \(i\) 相连的 所有点 \(x\) ,会有两种情况

  • \(x\) 还没有与别的点匹配
    直接将 \(x\) 与 \(i\) 匹配即可
  • \(x\) 已经与别的点匹配了
    我们设 \(x\) 原来与 \(y\) 匹配了
    那么我们 看能否 递归的让 \(y\) 重新找到一个点匹配,
    若能,则将 \(y\) 与那个点匹配,然后将 \(x\) 与 \(i\) 匹配
    若不能,那么 \(i\) 点无法从形成匹配

形式化的理解就是原本 \(x\) 与 \(y\) 在一对 ,现在 \(y\) 还有其他舞伴 ,就把 \(x\) 让给了 \(i\)

这样就能多出一个匹配,相当于找到一条 “增广路径”

点击查看匈牙利算法的代码
#include<bits/stdc++.h>
using namespace std;
int n,m,e;
struct edge
{
	int f,t;
};
edge edges[100000];
struct node
{
	vector<int> to;
	int match;
	bool vis;
	
};
node nodes[100000];
bool dfs(int x)
{
	int to;
	for(int ww=0;ww<nodes[x].to.size();ww++)//遍历这个点的所有能够到达的点 
	{
		to=edges[nodes[x].to[ww]].t;//能到的点 
		if(nodes[to].vis)//如果要去的点被遍历过 
		{
			continue;
		}
		nodes[to].vis=1;//标记要去的点 
		if(nodes[to].match==0||dfs(nodes[to].match))//如果要去的点没被匹配 或者 与其匹配的点还有别的选择即还能和别的点匹配 
		{
			nodes[to].match=x;//这两个点匹配 
			return 1;//返回可行 
		}
	}
	return 0;//不可行 
}
int main()
{
	ios::sync_with_stdio(0);
	cin>>n>>m>>e;
	int a,b;
	for(int ww=1; ww<=e; ww++)
	{
		cin>>a>>b;
		edges[ww].f=a;
		edges[ww].t=b;
		edges[ww+e].f=b;
		edges[ww+e].t=a;
		nodes[a].to.push_back(ww);
		nodes[b+n].to.push_back(ww+e);
	}
	int ans=0;
	for(int q=1;q<=n;q++)
	{
		for(int yy=1;yy<=m;yy++)
		{
			nodes[yy].vis=0;
		}
		if(dfs(q))
		{
			ans++;
		}
	}
	cout<<ans;


	return 0;
}

网络流做法

前置知识点: 网络流

事实上,二分图的最大匹配问题也可以转化为网络流来解决

我们假设有一个超级源点,将这个点向 \(G_1\) 中的每一个点 连一条 流量 为 \(1\) 的边,\(G_1,G_2\)之间的边的流量也为 \(1\) 然后 建立一个 超级汇点 将 \(G_2\) 中的每一个点 与超级汇点建立一条 流量 为 \(1\) 的边,二分图最大匹配的问题就被转化为了网络流的最大流问题

同时,这种做法能更好的处理一些匹配问题

若跑 EK 算法 时间复杂度 \(O(n m^2)\)
若跑 Dinic 算法 时间复杂度 \(O(\sqrt{n}m)\) ,非常优秀~

Part3:补充

二分图还有一点别的东西,比如:

二分图最小点覆盖

最小点覆盖就是要选最少的点,满足二分图的每条边至少有一个端点被选。

那么,先说结论: 最小点覆盖所需点数=最大匹配的边数

点击查看证明 将二分图点集分成左右两个集合,使得所有边的两个端点都不在一个集合。

考虑如下构造:从左侧未匹配的节点出发,按照匈牙利算法中增广路的方式走,即先走一条未匹配边,再走一条匹配边。由于已经求出了最大匹配,所以这样的「增广路」一定以匹配边结束,即增广路是不完整的。在所有经过这样「增广路」的节点上打标记。则最后构造的集合是:所有左侧未打标记的节点和所有右侧打了标记的节点。

首先,这个集合的大小等于最大匹配。左边未打标记的点都一定对应着一个匹配边(否则会以这个点为起点开始标记),右边打了标记的节点一定在一条不完整的增广路上,也会对应一个匹配边。假设存在一条匹配边左侧标记了,右侧没标记,左边的点只能是通过另一条匹配边走过来,此时左边的点有两条匹配边,不符合最大匹配的规定;假设存在一条匹配边左侧没标记,右侧标记了,那就会从右边的点沿着这条匹配边走过来,从而左侧也有标记。因此,每一条匹配的边两侧一定都有标记(在不完整的增广路上)或都没有标记,匹配边的两个节点中必然有一个被选中。

其次,这个集合是一个点覆盖。由于我们的构造方式是:所有左侧未打标记的节点和所有右侧打了标记的节点。假设存在左侧打标记且右侧没打标记的边,对于匹配边,上一段已经说明其不存在,对于非匹配边,右端点一定会由这条非匹配边经过,从而被打上标记。因此,这样的构造能够覆盖所有边。

同时,不存在更小的点覆盖。为了覆盖最大匹配的所有边,至少要有最大匹配边数的点数。

证明方式来自 OIwiki

二分图:二分图最大独立集

二分图最大独立集就是要求选最多的点,满足两两之间没有边相连。

结论:因为在最小点覆盖中,任意一条边都被至少选了一个顶点,所以对于其点集的补集,任意一条边都被至多选了一个顶点,所以不存在边连接两个点集中的点,且该点集最大。因此二分图中,\(最大独立集 = n- 最小点覆盖\)。


完结撒花!

标签:二分,ww,匹配,标记,int,笔记,学习,edges
From: https://www.cnblogs.com/sea-and-sky/p/18459348

相关文章

  • 【动物识别系统】Python+卷积神经网络算法+人工智能项目+深度学习+计算机课设项目
    一、介绍动物识别系统。本项目以Python作为主要编程语言,并基于TensorFlow搭建ResNet50卷积神经网络算法模型,通过收集4种常见的动物图像数据集(猫、狗、鸡、马)然后进行模型训练,得到一个识别精度较高的模型文件,然后保存为本地格式的H5格式文件。再基于Django开发Web网页端操作......
  • 【交通标志识别系统】Python+卷积神经网络算法+人工智能+深度学习+图像识别+计算机课
    一、介绍交通标志识别系统。本系统使用Python作为主要编程语言,在交通标志图像识别功能实现中,基于TensorFlow搭建卷积神经网络算法模型,通过对收集到的58种常见的交通标志图像作为数据集,进行迭代训练最后得到一个识别精度较高的模型文件,然后保存为本地的h5格式文件。再使用Dj......
  • Communication-Efficient Learning of Deep Networks from Decentralized Data论文阅
    联邦学习开山之作Communication-EfficientLearningofDeepNetworksfromDecentralizedDataabstractIntroductionTheFederatedAveragingAlgorithmExperimentalResultsConclusionsandFutureWorkCommunication-EfficientLearningofDeepNetworksfromDec......
  • 【海洋生物识别系统】Python+卷积神经网络算法+人工智能+深度学习+计算机毕设项目+Ten
    一、介绍海洋生物识别系统。以Python作为主要编程语言,通过TensorFlow搭建ResNet50卷积神经网络算法,通过对22种常见的海洋生物(‘蛤蜊’,‘珊瑚’,‘螃蟹’,‘海豚’,‘鳗鱼’,‘水母’,‘龙虾’,‘海蛞蝓’,‘章鱼’,‘水獭’,‘企鹅’,‘河豚’,‘魔鬼鱼’,‘......
  • 学习Opencv的第八天——优化Opencv在执行时的性能
    1、使用OpenCV衡量性能cv.getTickCount函数返回从参考事件(如打开机器的那一刻)到调用此函数那一刻之间的时钟周期数。因此,如果在函数执行之前和之后调用它,则会获得用于执行函数的时钟周期数。cv.getTickFrequency函数返回时钟周期的频率或每秒的时钟周期数。因此,要找到执行......
  • 强化学习: 传统控制类问题使用强化学习解决时对神经网络结构的依赖 —— 神经网络结构
    最近在看有关上个世纪中的写的关于使用神经网络的强化学习算法控制机械的论文,也就是使用传统的神经网络结构(没有CNN/LSTM模块)的稀疏连接的类似MLP的神经网络,使用这样的神经网络结构并用强化学习算法来训练控制机械的策略算法。看到一些上世纪90年代的基于神经网络的强化学习论文......
  • 基于QLearning强化学习的机器人避障和路径规划matlab仿真
    1.算法仿真效果matlab2022a仿真结果如下(完整代码运行后无水印):  2.算法涉及理论知识概要       强化学习是一种机器学习方法,它使智能体能够在与环境交互的过程中学习如何采取行动以最大化累积奖励。Q-Learning是一种无模型的强化学习算法,特别适合于离散动作空......
  • scrapy框架学习笔记
    scrapy运行机制详见Architectureoverview安装直接pipinstallscrapy即可使用命令行scrapystartprojectname命令创建一个新的Scrapy项目scrapycrawlSpiderName命令运行爬虫scrapyrunspiderSpiderName命令运行脚本。更多命令直接查Commandlinetool概述编写S......
  • Ai软件学习 I
     1.上图中的控制会打开菜单栏中下方空处,这是与PS不同的地方,在这里,这个地方用来存放画板,而PS是用来放工具属性的。2.Ai是由路径和锚点组成的,而PS是由像素组成的,所以Ai左侧小黑工具是用来控制路径的,也可以用作移动工具,小白工具是用来操作锚点的。     保存的时候,使......
  • Prompt 学习地图 | 框架思维
    该框架主要包括以下五个部分:背景B(Background)角色R(Role)目标O(Objectives)关键结果K(KeyResults)实验改进E(Evolve)框架解释背景(Background)背景信息部分提供关于请求的背景和上下文,它帮助ChatGPT更好地理解问题的背后意图和情境。例如,当你询问有关......