ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【luogu P6113】【UOJ 79】【模板】一般图最大匹配(带花树)

2021-09-30 17:34:11  阅读:188  来源: 互联网

标签:pre le int luogu fa P6113 79 find 1001


【模板】一般图最大匹配

题目链接:luogu P6113 / UOJ 79

题目大意

给你一个图,问你它的最大匹配。

思路

首先我们要知道带花树其实是在匈牙利的匹配算法上,特殊处理掉有奇环的情况。

那如果有奇环是怎么样的呢?
那我们也处理不了它,那我们就考虑把它缩成一个点,然后继续搞。

那我们可以这么搞,然后搞完之后,再把环展开。

那如何实现呢?
我们考虑搞一个队列,让队列的点都是黑色点,它的匹配点是白色点,其它没有颜色,那如果出现两个相邻同时黑色,那就要开花了(缩点)。

那要怎么开花呢,我们不能重构图,复杂度会很大,我们考虑用一个并查集进行维护,把同一个花中的点在并查集中合并。
那我们找环就是找你出问题的那两个点在 bfs 树上的 LCA,这个其实一步一步轮流跳就可以了。

然后接着考虑求出来了怎么开花。
我们考虑把 \(pre_i\) 这个东西叫做它的前驱,那如果我们出问题的点是 \(u,v\),我们就把它的 \(pre\) 互相连接。然后两边分别跳,把白色的点全部变成黑色的,然后扔进队列里面就可以了。
(因为这样后面就是两种路线都分别可以有路扩展了)

代码

#include<queue>
#include<cstdio>

using namespace std;

struct node {
	int to, nxt;
}e[100001];
int n, m, x, y, le[1001], KK;
int pp[1001], ans, fa[1001], tim;
int kd[1001], pre[1001], dfn[1001];
queue <int> q;

void add(int x, int y) {
	e[++KK] = (node){y, le[x]}; le[x] = KK;
	e[++KK] = (node){x, le[y]}; le[y] = KK;
}

int find(int x) {//并查集
	if (fa[x] == x) return x;
	return fa[x] = find(fa[x]);
}

int lca(int x, int y) {
	++tim;
	x = find(x); y = find(y);
	while (dfn[x] != tim) {//不停暴力往上跳
		dfn[x] = tim;
		x = find(pre[pp[x]]);
		if (y) swap(x, y);//交替跳
	}
	return x;
}

void blossom(int x, int y, int w) {
	while (find(x) != w) {
		pre[x] = y; y = pp[x];
		if (kd[y] == 2) kd[y] = 1, q.push(y);//强改颜色且入队
		if (find(x) == x) fa[x] = w;//连到根
		if (find(y) == y) fa[y] = w;
		x = pre[y];
	}
}

int wk(int x) {
	for (int i = 1; i <= n; i++)
		fa[i] = i, kd[i] = pre[i] = 0;
	while (!q.empty()) q.pop();
	
	q.push(x); kd[x] = 1;
	while (!q.empty()) {
		x = q.front();
		q.pop();
		
		for (int i = le[x]; i; i = e[i].nxt) {
			if (find(x) == find(e[i].to) || kd[e[i].to] == 2) continue;//在同一个花里面或形成了偶环
			
			if (!kd[e[i].to]) {
				kd[e[i].to] = 2; pre[e[i].to] = x;
				if (!pp[e[i].to]) {
					for (int k = e[i].to; k;) {//匈牙利的匹配过程
						int ty = pp[pre[k]];
						pp[k] = pre[k]; pp[pre[k]] = k;
						k = ty;
					}
					return 1;
				}
				kd[pp[e[i].to]] = 1; q.push(pp[e[i].to]);
			}
			else {//出现奇环
				int w = lca(x, e[i].to);//找到花根
				blossom(x, e[i].to, w);//两边反向展开
				blossom(e[i].to, x, w);
			}
		}
	}
	return 0;
}

int main() {
	scanf("%d %d", &n, &m);
	for (int i = 1; i <= m; i++) {
		scanf("%d %d", &x, &y);
		add(x, y);
	}
	
	for (int i = 1; i <= n; i++)
		if (!pp[i]) ans += wk(i);
	
	printf("%d\n", ans);
	for (int i = 1; i <= n; i++) printf("%d ", pp[i]);
	
	return 0;
}

标签:pre,le,int,luogu,fa,P6113,79,find,1001
来源: https://www.cnblogs.com/Sakura-TJH/p/luogu_P6113.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有