首页 > 其他分享 >二分图

二分图

时间:2023-10-18 20:12:48浏览次数:23  
标签:二分 return int dfs --- 匹配

二分图

前情提要:今日速查打二分图最大匹配,发现自己的匈牙利算法《学的非常好》,于是一怒之下写了这篇笔记

1.什么是二分图?

若一张无向图\(G\)的\(N\)个节点可分成\(A、B\)两个不相交的非空集合,并且同一集合内的点之间没有边相连,那称该图为二分图

性质:二分图中不存在奇环(一个点想回到自己的点集肯定会经过奇数条边)

2.二分图判定

1)染色法

从一个节点\(u\)染色,并把与其相连的所有点染上异色,若最后发现存在一个点被染上了两种不同颜色,则证明该图不是二分图,否则一定是二分图

实现:\(dfs\)

inline bool dfs(int u,int c){
	col[u]=c;
	for(auto v:G[u]){//分两类讨论:v点已被染色和未被染色.一定要从每个点出发把每条边都验证一下
		if(col[v] && col[v]==c) return 0;
		if(!col[v] && !dfs(v,3-c)) return 0;
	}
	return 1;
}
int main(){
	scanf("%d%d",&n,&m);
	F(i,1,m){
		scanf("%d %d",&u,&v);
		G[u].push_back(v);
		G[v].push_back(u);
	}
	if(dfs(1,1)) puts("YES");
	else puts("NO");
	return 0; 
}

2)扩展域并查集

扩展域并查集用以解决多个逻辑冲突问题:有两个集合,n个ai和bi,m个形如(ai,bi)的条件表示ai和bi不在同一集合中,问最终能否达到

思想:约束\(a,b\)不在一个集合中即就是将\(a\)与\(\neg b\)放入放入同一集合内,再将\(b\)与\(\neg a\)放入同一集合内,最后检查\(a\)与\(\neg a\),\(b\)与\(\neg b\)是否在同一集合中即可,实际编程中\(merge(a,\neg b)\)就是\(merge(a,b+n)\)

#include<bits/stdc++.h>
using namespace std;
#define F(i,l,r) for(int i=l;i<=r;++i)
const int N=205,M=5005;
int n,m,u,v,fa[N<<1];//并查集开两倍空间 
inline int getfather(int x){
	if(fa[x]!=x) fa[x]=getfather(fa[x]);
	return fa[x];
}
inline void add(int x,int y){
	x=getfather(x),y=getfather(y);
	if(x!=y) fa[x]=y; 
}
inline bool chk(int x,int y){ return getfather(x)==getfather(y); }
int main(){
	scanf("%d%d",&n,&m); F(i,1,n*2) fa[i]=i;
	F(i,1,m) {
		scanf("%d%d",&u,&v); ++u,++v;	
		if(u>v) swap(u,v);
		add(u,v+n);
	}
	F(i,1,n) if(chk(i,i+n)) return puts("NO"),0;
	puts("YES");
	return 0;
}

二分图最大匹配

对于G的子图M,若M中任意两边都没有公共端点,则称M是二分图的一种匹配,所有匹配中包含边数最多的一组匹配成为二分图的最大匹配

1)匈牙利算法:

思想:通过不断找增广路寻找更大的匹配,若无法找到增广路了,当前匹配就是最大匹配

交替路:非匹配边--->匹配边--->非匹配边--->匹配边--->非匹配边--->匹配边--->......

增广路:从非匹配点出发沿交替路到达另一非匹配点的路径

性质:增广路上的非匹配边总比匹配边多一条(两两为一组,最后一条非匹边会剩下),故 非匹 与 已匹 互换身份就能使匹配变大

实现:\(dfs\)(参考代码)或\(bfs\)

inline bool dfs(int u){
	for(int i=first[u];i;i=e[i].ne){
		int v=e[i].v; if(vis[v]) continue;vis[v]=1;
		if(!match[v] || dfs(match[v])) return match[v]=u,1;
	}
	return 0;
}
	idx=0,sum=0; mem(e); mem(match); mem(first);
	scanf("%d%d%d",&k,&m,&n); int u,v;
	F(i,1,k){
		scanf("%d%d",&u,&v);
		add(u,v);
	}
	F(i,1,m){
		mem(vis);
		if(dfs(i)) ++sum;
	}
	printf("%d\n",sum);

最坏情况下以每个点为起点寻找增广路会遍历整张图每条边(continue也会耗时所以注意不是\(O(N)\)),时间复杂度为\(O(M)\),故总时间复杂度为\(O(NM)\)

2)\(Dinic\)算法:学了再说

再忘我就再写,我还不信了这么简单个算法我能记不住?

标签:二分,return,int,dfs,---,匹配
From: https://www.cnblogs.com/superl61/p/17773221.html

相关文章

  • 二分模板
    二分答案的写法有很多模板,但使用的情况各不相同前两种模板:都是while(l<r),但是会有区别的,区别在代码注释中有体现。后两种模板:都是while(l<=r),也是在返回上有区别。这是最大值最小intmain(){ intl; intr; while(l<r) { intmid=(l+r)/2; if(check()......
  • C语言二分法
    ////main.c//BinarySearch////Createdbystevexiaohuzhaoon2023/10/16.//#include<stdio.h>//二分法查找指定元素在数组中出现的索引位置intBinarySearch(int*array,intlength,intk){intleft,right,mid,NotFound=-1;//设置......
  • 二分
    二分二分是一种基于一个具有非严格单调性的序列上进行搜索的算法,其复杂度为\(O(logn)\),在单调性的前提下,效率碾压遍历不知道多少倍。原理我们以下图中长度\(n=10\)的非严格单调递增序列\(P\)为例假设此时我们询问一个数\(x\),我们需要在序列中找到一个数\(y\),使得\(y......
  • 二分图备忘录
    本文是写给作者自己看的概念指一张无向图G中,N个节点可以划分为两个集合A,B集合A和B内部没有连边,A和B可以有连边(可以有空集)Q:为什么不用三分图:A:很简单,三分图分类更多,更麻烦。没有顺序关系有三种情况,有顺序关系则是六种(就像线段树不用三叉)一些叫法A集合内的点:左部点B集合内的......
  • 折半(二分)查找算法—高效搜索算法
    折半查找算法(BinarySearchAlgorithm)是一种高效的搜索算法,常用于已排序的数组或列表中进行查找操作。它的核心思想是通过比较中间元素与目标值的大小关系来确定目标值在数组的哪一部分,从而缩小搜索范围。本文将详细介绍折半查找算法的原理、实现以及应用场景。一、原理折半查找算......
  • 判断二分图的方法
    题目描述:龙龙得知2020年中国将有2000万至4000万男人娶不到老婆后。他打算好好调查一下是不是人们的感情出现问题。他对n个人进行调查,得到m条信息,每条信息表示为某两人曾经是情侣。由于他不知道这些人的性别,请你帮他判断一下,有没有同性是情侣的情况?对于100%的数据,n的范围[2,100000......
  • 二分模板
    整数二分边界boolcheck(intx){/*...*/}//检查x是否满足某种性质//区间[l,r]被划分成[l,mid]和[mid+1,r]时使用:intbsearch_1(intl,intr){while(l<r){intmid=l+r>>1;if(check(mid))r=mid;//check()判断mid是......
  • 算法0506 对数器 二分搜索
    对数器非常重要的自我验证代码正确性的方法在面试时或机试时写算法题,没有测试用例或者测试用例太少,导致巨大的数据量无法进行测试时。需要自己写测试用例数据时可以使用对数器。......
  • 学习C语言心得-自定义函数-对整形有序数组进行二分查找-二分法
    对整形有序数组进行二分查找#include<stdio.h>intfind(intarr[],intsz,intk){ intleft=0;intright=sz-1; while(left<=right) { intmid=left+right/2; if(k>arr[mid]) { left=mid+1; } if(k<arr[mid]) { right=mid......
  • 二分查找(浮点二分)
    一、算法简介浮点数二分相比与整数二分就要简单很多了,但是还是要注意范围的问题。以下给出一个小例子,求\(x\)的平方根,\(x\)的范围在\([0,10000]\)内:#include<iostream>#include<cmath>usingnamespacestd;intmain(){doublen;cin>>n;dou......