• 带花树算法学习笔记


    带花树算法

    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('
    ');
    	for(int i = 1; i <= n; ++i) pt(link[i]);
    	putchar('
    ');
    	return 0;
    }
    

    其实我也不太明白qwq

    -.-

  • 相关阅读:
    SQL Server 创建用户报错:消息 15023,级别 16,状态 1,第 1 行 用户、组或角色 'XXX' 在当前数据库中已存在。
    Win10安装sqlserver2014打开显示黑色界面,mardown打开显示报错
    Centos7磁盘超过2TB使用parted命令分区
    Html5學習重點清單
    jQuery源码学习扒一扒jQuery对象初使化
    jQuery源码学习
    算法排序之插入排序
    算法排序之冒泡排序
    Sublime Text 3 安装
    css布局你该了解的
  • 原文地址:https://www.cnblogs.com/zhltao/p/12659198.html
Copyright © 2020-2023  润新知