带花树算法学习笔记

带花树算法

AC 300祭

正文

主要思想是开花和并查集维护-.-

我们先模拟算法执行的过程

好吧,我并不是绘画流程图的样子。那就老老实实画图吧。

首先,对于每一个点点 bfs。

假设我们有一个图。

我们要在这个图上做一般图匹配。先从一节点开始,他找了二结点,嗯,没有匹配,我们就要路径取反,之后在搞

之后尝试寻找三节点的匹配,然后他找到了一结点,一结点的对应节点的颜色标记为 1 ,并入队。开始寻找2结点,发现他有颜色,且为一,得知是一个奇环,所以我们要先找到树根,然后树根应该是 3

对于 2 1 3 进行开花。此时找到的根应该是 3 我们缩环 2 3 1,二的前驱被标记为 3, 而fa[1],fa[2] 均为3, 但是找不匹配。。于是 34匹配了

然后最后执行九的过程,会有先找4 三入队。然后找 8 ,7入队。然后3是队首还是执行原来的过程1, 2, 3的开花。然后就蒙了可。。。

其实思想是一个用pre来记录路径,然后用 并查集来维护

/*
 * @Author: zhltao 
 * @Date: 2020-04-05 17:26:05 
 * @Last Modified by: zhltao
 * @Last Modified time: 2020-04-05 18:35:20
 */

#include <bits/stdc++.h>

using namespace std;

template <typename T>
inline T read()
{
	T x = 0;
	char ch = getchar();
	bool f = 0;
	while(ch < '0' || ch > '9')
	{
		f = (ch == '-');
		ch = getchar();
	}
	while(ch <= '9' && ch >= '0')
	{
		x = ((x + (x << 2)) << 1) + (ch - '0');
		ch = getchar();
	}
	return  f? -x : x;
}

template <typename T>
void put(T x)
{
	if(x < 0)
	{
		x = -x;
		putchar('-');
	}
	if(x < 10) {
		putchar(x + 48);
		return;
	}
	put(x / 10);
	putchar(x % 10 + 48);
	return ;
}

#define rd read <int>
#define pt(i) put <int> (i), putchar(' ')

typedef long long ll;
typedef double db;
typedef long double ldb;
typedef unsigned long long ull;
typedef unsigned int ui;

const int Maxn = 505, Maxm = 124750 << 1 | 1;

int fa[Maxn], link[Maxn], ans, h[Maxn], cnt, n, m, color[Maxn], pre[Maxn], dfn[Maxn];

queue <int> q;

struct Edge
{
	int to, lac;
	void insert(int x, int y) { to = y; lac = h[x]; h[x] = cnt++; }
}edge[Maxm];
// 添边
inline void add_edge(int x, int y) { return edge[cnt].insert(x, y), edge[cnt].insert(y, x); }
// 并查集
int find(int x) { return x == fa[x] ? fa[x] : fa[x] = find(fa[x]); }

int ord;
// 求树根
inline int lca(int x, int y)
{
	/*
	首先如果是花套花
	则要求两个分别的树根
	再把新的求出来,要不然会造成染色错误
	*/
	for(++ord, x = find(x), y = find(y); dfn[x] != ord;)
	{
		//打上时间戳
		dfn[x] = ord;
		// 对于这个直接就找树根。就是两个交替做
		x = find(pre[link[x]]);
		// 万一到了增广路的头的话,也是 s
		// 找到共同的起点,在一个比如此时 x 已经是 dfn[x] = cnt 了
		// 到下边就会 swap 在做一遍
		if(y) swap(x, y);
		// 退出时在 dfn[x] = ord,即 fa[x] = x 时
	}
	return x;
}

void boss(int x, int y, int z)
{
	// 必须还是要 find
	while(find(x) != z)
	{
		pre[x] = y;
		y = link[x];
		if(!(color[y] ^ 2))
		{
			// 在花上且为 T 的点入队,他们可以找匹配
			color[y] = 1;
			q.push(y);
		}
		if(!(find(x) ^ x)) fa[x] = z;
		// 如果这个点的是树根的话,就把树根的树根改成 z
		if(!(find(y) ^ y)) fa[y] = z;
		x = pre[y];
	}
}
bool bfs(int s)
{
	for(int i = 1; i <= n; ++i) fa[i] = i, color[i] = pre[i] = 0;
	while(!q.empty()) q.pop();
	for(color[s] = 1, q.push(s); !q.empty(); q.pop())
	{
		int fr = q.front(), to;
		for(int i = h[fr]; i != -1; i = edge[i].lac)
		{
			// 原来在一个花 或这是 T 点
			if(find(fr) == find(to = edge[i].to) || color[to] == 2) continue;
			// 没打过标记
			if(!color[to])
			{
				// 打上标记,记录前驱
				color[to] = 2, pre[to] = fr;
				if(!link[to])
				{ // 路径取反
					for(int x = to, last, y; x; x = last) last = link[y = pre[x]], link[x] = y, link[y] = x;
					return 1;
				}
				// 不行,就入队,打标记
				color[link[to]] = 1;
				q.push(link[to]);
			}
			else
			{
				// 招树根
				int z = lca(fr, to);
				// 开花
				boss(fr, to, z);
				boss(to, fr, z);
			}
		}
	}
	return 0;
}


int main()
{
#ifdef _DEBUG
	freopen("in.txt", "r", stdin);
#endif
	n = rd();
	m = rd();
	memset(h, -1, sizeof h);
	while(m--)
		add_edge(rd(), rd());
	for(int i = 1; i <= n; ++i)
	ans += (!link[i] && bfs(i));
	put <int> (ans), putchar('\n');
	for(int i = 1; i <= n; ++i) pt(link[i]);
	putchar('\n');
	return 0;
}

其实我也不太明白qwq

-.-

猜你喜欢

转载自www.cnblogs.com/zhltao/p/12659198.html