一般图最大匹配

先说下思想:
一般图与二分图不同的地方就在于奇环, 考虑出现奇环如何处理, 我们发现我们从一个点搜到一个奇环时, 最先搜到的地方我们称为花基, 那么会发现从花基从逆时针, 顺时针搜到这个奇环中的某一个点u时, 会发现u被我们要求标成两种不同的颜色, 这时我们能够发现奇环, 将其缩成一个点, 可以证明缩点后不会对増广产生影响, 那么我们缩点时需要做些什么:

  • 首先, 我们用pre记录增广路上的前驱, 这样会方便我们的反边及展开花
  • 然后, 在这个花中我们用并查集来表示他们同属于一朵花
  • 花中的节点非外结点全部改成外结点(即放入队列的点), 因为花的任何一个位置都可以延伸出去, 并且花嵌花并不影响, 还有一点就是防止pre从花基或大花指向小花, 要注意判断
  • 展开时同 二分图
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

const int MAXN = 5e2 + 10;
const int MAXM = MAXN * MAXN;
#define debug(x) cerr << "!" << endl
#define rep(i, s, t) for(int i = s; i <= t; ++i)
#define erep(i, u) for(int i = Begin[u]; i ^ (-1); i = Next[i])

#define C c = getchar()
template<typename T>
T read() {
    char C; T x = 0, f = 1;
    while(c < '0' || c > '9') f = c=='-'?-1:1, C;
    while(c >= '0' && c <= '9') x = x*10 + c-'0', C;
    return x * f;
}

int n, m, flag;
namespace Bloosom {
    int fa[MAXN], vis[MAXN];
    int pre[MAXN], match[MAXN], color[MAXN];
    int e, Begin[MAXN], to[MAXM], Next[MAXM];

    void add(int x, int y) {
        to[e] = y;
        Next[e] = Begin[x];
        Begin[x] = e++;
    }

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

    queue<int> Q;
#define FILL(a, b) memset(a, b, sizeof a)
    void init() {
        FILL(pre, 0);
        FILL(color, -1);
        rep(i, 1, n) fa[i] = i;
        while(!Q.empty()) Q.pop();
    }

    int Lca(int u, int v) {
        FILL(vis, 0); int lca = 0;
        for(; u; u = pre[match[u]]) u = find(u), vis[u] = 1;
        for(; v; v = pre[match[v]]) 
            {v = find(v); if(vis[v]) {lca = v; break;}}
        return lca;
    }

    void Union(int u, int F) {
        for(; u ^ F; u = pre[match[u]]) {
            int x = match[u], y = pre[x];
            int a = find(u), b = find(x), c = find(y);

            if(find(y) ^ F) pre[y] = x;
            if(color[x] == 1) color[x] = 0, Q.push(x);
            fa[a] = b; fa[b] = c;
        }
    }

    void contract(int x, int y) {
        int lca = Lca(x, y);
        if(find(x) ^ lca) pre[x] = y;
        if(find(y) ^ lca) pre[y] = x;
        Union(x, lca);
//      if(flag) debug();
        Union(y, lca);
    }

    void BFS(int S) {
        init();
        Q.push(S); color[S] = 0;
        while(!Q.empty()) {
            int u = Q.front(); Q.pop();
            erep(i, u) {
                int v = to[i];
                if(match[v] == u || color[v] == 1
                || find(u) == find(v)) continue;

                if(!color[v]) {
                    //flag = (S == 11 && v == 7);
                    contract(u, v);
                }else if(match[v]) {
                    pre[v] = u;
                    color[v] = 1;
                    color[match[v]] = 0;
                    Q.push(match[v]);
                }else {
                    pre[v] = u;
                    int x = u, y = v;
                    for(;y;) {
                        int Nexty = match[x], Nextx = pre[Nexty];
                        match[y] = x, match[x] = y;
                        x = Nextx, y = Nexty;
                    }
                    return ;
                }
            }
        }
    }
};
using namespace Bloosom;

int main() {
#ifndef ONLINE_JUDGE
    freopen("input.in", "r", stdin);
    freopen("res.out", "w", stdout);
#endif
    FILL(Begin, -1);
    n = read<int>(), m = read<int>();
    rep(i, 1, m) {
        int u = read<int>(), v = read<int>();
        add(u, v), add(v, u);
    }

    int Ans = 0;
    rep(i, 1, n) if(!match[i]) BFS(i);
    rep(i, 1, n) Ans += (bool) match[i];

    printf("%d\n", Ans/2);
    rep(i, 1, n) printf("%d%c", match[i], i ^ n? ' ' : '\n');
    return 0;
}
posted @ 2017-02-24 18:50  pbvrvnq  阅读(99)  评论(0编辑  收藏  举报