图论算法二分图:染色法与匈牙利算法

Posted karshey

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了图论算法二分图:染色法与匈牙利算法相关的知识,希望对你有一定的参考价值。

AcWing 860. 染色法判定二分图

AcWing 860. 染色法判定二分图

二分图

二分图就是可以把所有点划分到两边集合中去,使得所有的边在两个集合外且在两个集合之间,集合内部没有边的图

二分图的性质:当且仅当图中不含有奇数环
(奇数环:环中边的数量是奇数)

证明:
必要性:二分图中不含有奇数环。
反证法。
假设有一个奇数环是二分图,设有两个集合A,B。
第1、3、5…个点在A中,第2、4、6个点在B中。由于是二分图,且起点(1号点)在A中,则最后一个点一定在B中。那么点的数量一定为偶数,与假设相矛盾。
因此二分图不会含有奇数环。

充分性:若一个图不含有奇数环,则它是二分图。
构造法。
假设有一个图不含奇数环,设有两个集合A,B。
遍历图,若遇到一个点没有被加入集合中,则把它放进A集合,把与这个点相连的所有点放入B集合。(则一条边的两个点不会在同一个集合,这是一个染色的过程)


则,图中任意一点的颜色确定了,则整个图的颜色都确定了。
由于图中不含有奇数环,整个染色过程不会有矛盾。(也可以反证法)

也就是说,如果一个图可以用染色法染一遍且没有矛盾发生,则它是二分图;若出现了矛盾,则它不是二分图。

染色法模板:

for(int i=1;i<=n;i++)
	if(i没有被染色)
		dfs(i);

题目链接

注意:
存图用链式前向星
memset(h,-1,sizeof(h))来初始化。
(虽然memset是按位赋值的,但由于这里赋值的参数是-1,总的值也是-1,不信可以试试)

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define fir(i,a,n) for(int i=a;i<=n;i++)
#define fi first
#define se second
#define pii pair<int,int>
#define pb push_back
const int N=1e5+10,M=2*N;
int n,m;

int e[M],ne[M],h[N],idx;
void add(int a,int b)

	e[idx]=b;
	ne[idx]=h[a];
	h[a]=idx++;


int flag;
int st[N];//每个点的颜色:1 2 
void dfs(int u,int c)

	if(flag) return;
	st[u]=c;
	
	for(int i=h[u];i!=-1;i=ne[i])
	
		int j=e[i];
		if(!st[j]) dfs(j,3-c);
		else if(st[j]==c)
		
			flag=1;return;
							
	

int main()

	cin>>n>>m;
	memset(h,-1,sizeof(h));//链式前向星的初始化 
	fir(i,1,m)
	
		int a,b;cin>>a>>b;		
		add(a,b);add(b,a);
	
	
	flag=0;
	for(int i=1;i<=n;i++)
	
		if(flag) break;
		if(!st[i]) dfs(i,1);
	
	if(flag) cout<<"No";
	else cout<<"Yes";
	return 0;

AcWing 861. 二分图的最大匹配(匈牙利算法)

在一个比较快的时间内告诉我们左右的匹配成功最大的数量。
匹配指的是边。匹配成功指的是不存在两条边共用一个点

通俗的理解:
如图,有四个男生和四个女生,最多可以成功多少对恋爱关系使得不存在脚踏多只船的情况?

算法思路:(如图)

  1. 遍历左集合中每一个男生,如果他看上的女生没有与其他人匹配则匹配成功。
  2. 如果他看上的女生已经匹配成功了,那么就判断该女生匹配的男生能不能换个人。

时间复杂度:O(n*m)

注意:存边的时候只需要存左边指向右边即可。
因为遍历的是所有左边的点,需要判断的是左边指向右边的点是否已匹配。不需要用到右边指向左边的点。

AcWing 861. 二分图的最大匹配

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define fir(i,a,n) for(int i=a;i<=n;i++)
const int N=500+10,M=1e5+10;
int n1,n2,m;

int h[N],e[M],ne[M],idx;//h是点,e和ne是边 
int match[N];//右边的点所匹配的左边的点 
int st[N];//该点是否已经访问过 
void add(int a,int b)

	e[idx]=b;ne[idx]=h[a];h[a]=idx++;


int find(int x)//为x点找匹配的点 

	for(int i=h[x];i!=-1;i=ne[i])
	
		int j=e[i];
		if(!st[j]) 
		
			st[j]=1;
			//核心 
			if(!match[j]||find(match[j]))//该点没有匹配 或匹配的对象可以重新匹配 
			
				match[j]=x;
				return 1;
			
		
	
	return 0;

int main()
	
	scanf("%d%d%d",&n1,&n2,&m);
	memset(h,-1,sizeof(h));
	while(m--)
	
		int a,b;scanf("%d%d",&a,&b);add(a,b);
	
	
	int ans=0;
	for(int i=1;i<=n1;i++)//遍历每一个左集合 
	
		memset(st,false,sizeof(st));
		if(find(i)) ans++;
	
	cout<<ans;
	return 0;

以上是关于图论算法二分图:染色法与匈牙利算法的主要内容,如果未能解决你的问题,请参考以下文章

第三章 搜索与图论

算法基础班 1.快速排序

图论二分图的应用(染色法判断二分图,最大匹配,最小点覆盖,最大独立集,最小路径点覆盖,最小路径重复点覆盖)

图论二分图的应用(染色法判断二分图,最大匹配,最小点覆盖,最大独立集,最小路径点覆盖,最小路径重复点覆盖)

二分图算法手把手教你学会:染色法(判断二分图)匈牙利算法(二分图的最大匹配)

图论-二分图匹配-匈牙利算法