【UOJ #79】一般图最大匹配 带花树模板

http://uoj.ac/problem/79
带花树模板,做法详见cyb的论文或fhq的博客。
带花树每次对一个未盖点bfs增广,遇到奇环就用并查集缩环变成花(一个点),同时记录每个点的Next(表示匹配),状态s(-1表示这个点没访问过,0表示这个点可以搜另一条相邻的未盖边,1表示这个点不能用于搜另一条相邻的未盖边),pre数组(u原先的匹配是Next[u],增广时u的匹配断掉了,u就与pre[u]进行匹配,即Next[u]=pre[u],Next[pre[u]]=u)。从一个点pre和Next交替的走出来的路径表示一条通往bfs树的根的路径(用于找到另一个未盖点后进行增广)。
时间复杂度\(O(n^3)\)

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int N = 503;
const int M = 130003;

struct node {int nxt, to;} E[M << 1];
int Next[N], cnt = 0, point[N], n, m, qu[N], s[N], pre[N], fa[N];

void ins(int u, int v) {E[++cnt] = (node) {point[u], v}; point[u] = cnt;}

int find(int x) {return fa[x] == x ? x : (fa[x] = find(fa[x]));}

int tim = 0, vis[N];
int getlca(int u, int v) {
    ++tim;
    while (true) {
        if (u) {
            if (vis[u] == tim) return u;
            vis[u] = tim;
            u = find(pre[Next[u]]);
        }
        swap(u, v);
    }
}

int p, q;
void blossom(int u, int v, int lca) {
    while (find(u) != lca) {
        pre[u] = v;
        v = Next[u];
        if (s[v] == 1) {s[v] = 0; if (++q == N) q = 0; qu[q] = v;}
        if (fa[v] == v) fa[v] = lca;
        if (fa[u] == u) fa[u] = lca;
        u = pre[v];
    }
}

int match(int x) {
    memset(s + 1, -1, sizeof(int) * n);
    for (int i = 1; i <= n; ++i) fa[i] = i;
    int u, v; p = 0; q = 1;
    s[qu[1] = x] = 0; pre[x] = 0;
    
    while (p != q) {
        if (++p == N) p = 0; u = qu[p];
        for (int i = point[u]; i; i = E[i].nxt) {
            v = E[i].to;
            if (s[v] == -1) {
                s[v] = 1; pre[v] = u;
                if (!Next[v]) {
                    int last;
                    while (u) {
                        last = Next[u];
                        Next[u] = v; Next[v] = u;
                        u = pre[v = last];
                    }
                    return 1;
                }
                s[Next[v]] = 0; if (++q == N) q = 0; qu[q] = Next[v];
            } else if (s[v] == 0 && find(u) != find(v)) {
                int lca = getlca(fa[u], fa[v]);
                blossom(u, v, lca);
                blossom(v, u, lca);
            }
        }
    }
    
    return 0;
}

int main() {
    scanf("%d%d", &n, &m);
    int u, v;
    for (int i = 1; i <= m; ++i) {
        scanf("%d%d", &u, &v);
        ins(u, v); ins(v, u);
    }
    
    int ans = 0;
    for (int i = 1; i <= n; ++i)
        if (!Next[i])
            ans += match(i);
    
    printf("%d\n", ans);
    for (int i = 1; i <= n; ++i)
        printf("%d ", Next[i]);
    return 0;
}

转载于:https://www.cnblogs.com/abclzr/p/6555301.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值