[模板] 匈牙利算法&&二分图最小字典序匹配

匈牙利算法

简介

匈牙利算法是一种求二分图最大匹配的算法.

时间复杂度: 邻接表/前向星: \(O(n * m)\), 邻接矩阵: \(O(n^3)\).
空间复杂度: 邻接表/前向星: \(O(n + m)\), 邻接矩阵: \(O(n^2)\).

它的主要思路就是对每个点寻找增广路, 尝试改变之前的选择, 判断是否可行.

事实上, 利用dinic/isap跑二分图有 \(O(n * \sqrt{m})\) 的优秀复杂度(不会证), 因此匈牙利算法仅用于少数特殊情况↓

代码

int to[nsz][nsz]; //邻接表
int vi[nsz],mat[nsz];
bool arg(int p){
	rep(i,1,to[0]){
		int v=to[p][i];
		if(vi[v])continue;
		vi[v]=1;
		if(mat[v]==0||arg(mat[v])){
			mat[v]=p,mat[p]=v;
			return 1;
		}
	}
	return 0;
}

int hung(){
	int res=0;
	repdo(i,1,n){
		memset(vi,0,sizeof(vi));
		res+=arg(i);
	}
	return res;
}

二分图最小字典序匹配

简介

这就是上面说的特殊情况:P

考虑匈牙利算法的过程: 将每一个点尝试增广, 同时改变之前的点的匹配.

因此, 我们可以考虑将所有点的出边按标号排序, 逆向遍历每一个点, 并按标号顺序尝试增广.
显然, 第一个点的匹配一定是它能匹配到的最小标号, 第二个点的匹配是满足第一个点时的最小标号, 以此类推.

代码

//[NOI2009] 变换序列
int to[nsz][3];
int vi[nsz],mat[nsz];
bool arg(int p){
	rep(i,0,1){
		int v=to[p][i];
		if(vi[v])continue;
		vi[v]=1;
		if(mat[v]==0||arg(mat[v])){
			mat[v]=p,mat[p]=v;
			return 1;
		}
	}
	return 0;
}

int hung(){
	int res=0;
	repdo(i,n-1,0){
		memset(vi,0,sizeof(vi));
		res+=arg(i);
	}
	return res;
}

参考资料

posted @ 2019-01-10 14:15  Ubospica  阅读(670)  评论(0编辑  收藏  举报