首页 > 其他分享 >一般图最大匹配

一般图最大匹配

时间:2023-08-28 22:14:31浏览次数:28  
标签:pre 匹配 最大 增广 int 一般 奇环 find

匈牙利算法寻找的增广路是有向的,其中匹配边的方向唯一,故匈牙利算法适配二分图的匹配。

对于存在奇环的一般图,匹配边在增广路中的方向不唯一,不符合匈牙利算法中 “一个点不能被访问两次” 的限制,故一般图最大匹配不能使用匈牙利算法。


带花树算法

一般图与二分图区别在于奇环的有无,对于一个大小为 \(2k+1\) 的奇环,其最多只有 \(k\) 对匹配,所以有至少一个节点能向外匹配,它可以是奇环内的任意节点。

求最大匹配时,一个奇环和一个节点等价,可以将对奇环缩点后当成二分图继续匹配。

把奇环缩成的点称为花,对原图用 bfs 寻找增广路,这棵节点可能是花的 bfs 树称带花树。

在带花树上 bfs,对访问到的节点进行黑白染色。

若起点为黑点,那么黑 \(\rightarrow\) 白的边为非匹配边,白 \(\rightarrow\) 黑的边为匹配边。

遍历黑点的出边,若访问到的点未被染色且未被匹配,此时就找到一条增广路。如果被匹配了,将该点染白,将该点的匹配点染黑并加入队列。

若访问到白点,此时找到一个偶环,不需要处理。若访问到黑点就找到了奇环,在 bfs 树上这两个点的 \(lca\) 可以确定奇环的位置,用并查集将它们缩起来。缩起来的环为黑点,将环上的白点改为黑点并加入队列中,也叫做开花。

遍历增广路可以对每个点存一个前驱 \(pre\) 表示从该点出发走一条非匹配边应该到达哪个节点。被缩掉的环的非匹配边的 \(pre\) 是双向的。

求 \(lca\) 时,每次用 \(pre\) 和 \(match\) 轮流往上跳,边跳边打标记,碰上了就是 \(lca\).

相当于暴力跳,跳完后缩点使得每次总点数都减少,时间均摊 \(O(1)\).

同理开花的复杂度也是均摊 \(O(1)\) 的。

类似启发式合并,通过缩环减少一个点的复杂度均摊 \(O(\log n)\),找到增广路的最劣复杂度为 \(O(n+m)\).

故用带花树算法求一般图最大匹配的复杂度为 \(O(n\times(n\log n+m))=O(n^2\log n+nm)\),由于跑不满可以当作 \(O(nm)\).


P6113 【模板】一般图最大匹配

#include<bits/stdc++.h>
#define N 1010
using namespace std;
int read(){
	int x=0,w=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
	while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return x*w;
}
int n,m,ans;
vector<int>e[N];
int f[N],match[N];
int tag[N],tim,col[N],pre[N];
queue<int>q;
int find(int x){
	return x==f[x]?x:f[x]=find(f[x]);
}
int lca(int x,int y){
	tim++;
	while(true){
		if(x){
			x=find(x);
			if(tag[x]==tim)return x;
			tag[x]=tim,x=pre[match[x]];
		}
		swap(x,y);
	}
	return -1;
}
void blossom(int x,int y,int w){
	while(find(x)!=w){
		pre[x]=y,y=match[x];
		col[y]=1,q.push(y);
		if(find(x)==x)f[x]=w;
		if(find(y)==y)f[y]=w;
		x=pre[y];
	}
}
bool bfs(int s){
	if((ans+1)*2>n)return false;
	for(int i=1;i<=n;i++)f[i]=i,col[i]=pre[i]=0;
	while(!q.empty())q.pop();
	q.push(s),col[s]=1;
	while(!q.empty()){
		int u=q.front();q.pop();
		for(int v:e[u]){
			if(find(u)==find(v)||col[v]==2)continue;
			if(!col[v]){
				col[v]=2,pre[v]=u;
				if(!match[v]){
					int x=v,y,z;
					while(x){
						y=pre[x],z=match[y];
						match[x]=y,match[y]=x;
						x=z;
					}
					return true;
				}
				col[match[v]]=1,q.push(match[v]);
			}
			else{
				int w=lca(u,v);
				blossom(u,v,w),blossom(v,u,w);
			}
		}
	}
	return false;
}
int main(){
	n=read(),m=read();
	for(int i=1,u,v;i<=m;i++){
		u=read(),v=read();
		e[u].push_back(v),e[v].push_back(u);
	}
	for(int i=1;i<=n;i++)
		if(!match[i])ans+=bfs(i);
	printf("%d\n",ans);
	for(int i=1;i<=n;i++)
		printf("%d ",match[i]);
	printf("\n");
	
	return 0;
}

标签:pre,匹配,最大,增广,int,一般,奇环,find
From: https://www.cnblogs.com/SError0819/p/17663478.html

相关文章

  • python中输出键最大、最小的项
     001、输出键最大的项a、>>>dict1={"c":30,"a":40,"b":80,"d":20,"e":60}>>>dict1{'c':30,'a':40,'b':80,'d':20,'e':60}>>&......
  • python中输出字典中值最大或最小的项
     001、输出值最大的项a、>>>dict1={"c":30,"a":40,"b":80,"d":60}##测试字典>>>dict1{'c':30,'a':40,'b':80,'d':60}>>>max_value=max(dict......
  • 一般图最大匹配学习笔记
    Uoj#79LuoguP6113带花树算法(匈牙利算法\(Pro~max\))我们考虑现在访问到\(u\)点(黑色),\(u\)连向\(v\)点,分类讨论\(v\)点。1、\(v\)点没有被匹配过,直接令\(u\)点和\(v\)点匹配,然后更新答案2、\(v\)点匹配过,但之前还未被访问过,那么把\(v\)点染成白色,然后把\(v\)......
  • 在传球游戏中最大化函数值
    给你一个长度为n下标从0开始的整数数组receiver和一个整数k总共有n名玩家,玩家编号互不相同,且为[0,n-1]中的整数。你需要从n名玩家中选择一名玩家作为游戏开始时唯一手中有球的玩家,球会被传恰好k次定义函数f(x)表示从编号为x的玩家开始,k次传球内所有......
  • 最大子矩阵和
    Blah数集大数学家高斯小时候偶然间发现一种有趣的自然数集合Blah,对于以a为基的集合Ba定义如下:(1)a是集合Ba的基,且a是Ba的第一个元素;(2)如果x在集合Ba中,则2x+1和3x+1也都在集合Ba中;(3)没有其他元素在集合Ba中了。现在小高斯想知道如果将集合Ba中元素按照升序排列,第N个元素会是......
  • Innodb引擎中B+树一般有几层?能容纳多少数据量?
    1、页在MySQL中InnoDB存储引擎的最小存储单元是页(大小默认是16k,可通过参数设置)。页可用于存放B+树叶节点数据,也可用于存放B+树非叶节点的“键+指针”(也就是路径节点)。在查找数据时一次页的查找代表一次IO,一般B+树高大约为1~3层,所以通过主键索引查询通常只需要1~3次IO......
  • 效果器——反向生成匹配响度
    反向就是从尾到头播放生成的时候,光标一定要放在最前面就在最前面生成了一个噪音匹配响度把音乐丢进去,运行声音就会平滑一点听起来更顺耳......
  • 原生家庭缺爱的人,一般身体都很弱,所以事业上很难有成就。
    1,836人赞同了该回答原生家庭缺爱的人,一般身体都很弱,所以事业上很难有成就。 这类人特别容易疲惫,稍微干点活,就觉得好累好累,就连稍微打扫下房间,都累到不行。想刷手机放松一下,可是越刷越累,不刷手机又不知道干嘛去,什么也都干不了,睡也睡不着,睡不安稳,只能继续刷手机,陷入一种......
  • 剑指 Offer 17. 打印从1到最大的n位数(简单)
    题目:classSolution{public:vector<int>printNumbers(intn){vector<int>result;intmax=pow(10,n)-1;//最大n位数的求法for(inti=1;i<=max;i++){result.push_back(i);}returnresul......
  • Leetcode_485. 最大连续 1 的个数
    题目描述给定一个二进制数组,计算其中最大连续1的个数。示例:输入:[1,1,0,1,1,1]输出:3解释:开头的两位和最后的三位都是连续1,所以最大连续1的个数是3.提示:输入的数组只包含0和1。输入数组的长度是正整数,且不超过10,000。参考实现示例1由于要累计最大连......