带花树模板

带花树模板,用来解决一般图的最大匹配。(可以是带权图)

#include<bits/stdc++.h>
using namespace std;

struct Blossom_algorithm
{
    typedef long long s64;

    const static int INF = 2147483647;
    const static int MaxN = 400;
    const static int MaxM = 79800;

    template <class T>
    inline void tension(T &a, const T &b)
    {
        if (b < a)
            a = b;
    }
    template <class T>
    inline void relax(T &a, const T &b)
    {
        if (b > a)
            a = b;
    }
    template <class T>
    inline int size(const T &a)
    {
        return (int)a.size();
    }

    const static int MaxNX = MaxN + MaxN;

    struct edge
    {
        int v, u, w;

        edge(){}
        edge(const int &_v, const int &_u, const int &_w)
            : v(_v), u(_u), w(_w){}
    };

    int n;
    edge mat[MaxNX + 1][MaxNX + 1];

    int n_matches;
    s64 tot_weight;
    int mate[MaxNX + 1];//mate[i]表示i的另一个匹配点,0代表未匹配
    int lab[MaxNX + 1];

    int q_n, q[MaxN];
    int fa[MaxNX + 1], col[MaxNX + 1];
    int slackv[MaxNX + 1];

    int n_x;
    int bel[MaxNX + 1], blofrom[MaxNX + 1][MaxN + 1];
    vector<int> bloch[MaxNX + 1];

    inline int e_delta(const edge &e) // does not work inside blossoms
    {
        return lab[e.v] + lab[e.u] - mat[e.v][e.u].w * 2;
    }
    inline void update_slackv(int v, int x)
    {
        if (!slackv[x] || e_delta(mat[v][x]) < e_delta(mat[slackv[x]][x]))
            slackv[x] = v;
    }
    inline void calc_slackv(int x)
    {
        slackv[x] = 0;
        for (int v = 1; v <= n; v++)
            if (mat[v][x].w > 0 && bel[v] != x && col[bel[v]] == 0)
                update_slackv(v, x);
    }

    inline void q_push(int x)
    {
        if (x <= n)
            q[q_n++] = x;
        else
        {
            for (int i = 0; i < size(bloch[x]); i++)
                q_push(bloch[x][i]);
        }
    }
    inline void set_mate(int xv, int xu)
    {
        mate[xv] = mat[xv][xu].u;
        if (xv > n)
        {
            edge e = mat[xv][xu];
            int xr = blofrom[xv][e.v];
            int pr = find(bloch[xv].begin(), bloch[xv].end(), xr) - bloch[xv].begin();
            if (pr % 2 == 1)
            {
                reverse(bloch[xv].begin() + 1, bloch[xv].end());
                pr = size(bloch[xv]) - pr;
            }

            for (int i = 0; i < pr; i++)
                set_mate(bloch[xv][i], bloch[xv][i ^ 1]);
            set_mate(xr, xu);

            rotate(bloch[xv].begin(), bloch[xv].begin() + pr, bloch[xv].end());
        }
    }
    inline void set_bel(int x, int b)
    {
        bel[x] = b;
        if (x > n)
        {
            for (int i = 0; i < size(bloch[x]); i++)
                set_bel(bloch[x][i], b);
        }
    }

    inline void augment(int xv, int xu)
    {
        while (true)
        {
            int xnu = bel[mate[xv]];
            set_mate(xv, xu);
            if (!xnu)
                return;
            set_mate(xnu, bel[fa[xnu]]);
            xv = bel[fa[xnu]], xu = xnu;
        }
    }
    inline int get_lca(int xv, int xu)
    {
        static bool book[MaxNX + 1];
        for (int x = 1; x <= n_x; x++)
            book[x] = false;
        while (xv || xu)
        {
            if (xv)
            {
                if (book[xv])
                    return xv;
                book[xv] = true;
                xv = bel[mate[xv]];
                if (xv)
                    xv = bel[fa[xv]];
            }
            swap(xv, xu);
        }
        return 0;
    }

    inline void add_blossom(int xv, int xa, int xu)
    {
        int b = n + 1;
        while (b <= n_x && bel[b])
            b++;
        if (b > n_x)
            n_x++;

        lab[b] = 0;
        col[b] = 0;

        mate[b] = mate[xa];

        bloch[b].clear();
        bloch[b].push_back(xa);
        for (int x = xv; x != xa; x = bel[fa[bel[mate[x]]]])
            bloch[b].push_back(x), bloch[b].push_back(bel[mate[x]]), q_push(bel[mate[x]]);
        reverse(bloch[b].begin() + 1, bloch[b].end());
        for (int x = xu; x != xa; x = bel[fa[bel[mate[x]]]])
            bloch[b].push_back(x), bloch[b].push_back(bel[mate[x]]), q_push(bel[mate[x]]);

        set_bel(b, b);

        for (int x = 1; x <= n_x; x++)
        {
            mat[b][x].w = mat[x][b].w = 0;
            blofrom[b][x] = 0;
        }
        for (int i = 0; i < size(bloch[b]); i++)
        {
            int xs = bloch[b][i];
            for (int x = 1; x <= n_x; x++)
                if (mat[b][x].w == 0 || e_delta(mat[xs][x]) < e_delta(mat[b][x]))
                    mat[b][x] = mat[xs][x], mat[x][b] = mat[x][xs];
            for (int x = 1; x <= n_x; x++)
                if (blofrom[xs][x])
                    blofrom[b][x] = xs;
        }
        calc_slackv(b);
    }
    inline void expand_blossom1(int b) // lab[b] == 1
    {
        for (int i = 0; i < size(bloch[b]); i++)
            set_bel(bloch[b][i], bloch[b][i]);

        int xr = blofrom[b][mat[b][fa[b]].v];
        int pr = find(bloch[b].begin(), bloch[b].end(), xr) - bloch[b].begin();
        if (pr % 2 == 1)
        {
            reverse(bloch[b].begin() + 1, bloch[b].end());
            pr = size(bloch[b]) - pr;
        }

        for (int i = 0; i < pr; i += 2)
        {
            int xs = bloch[b][i], xns = bloch[b][i + 1];
            fa[xs] = mat[xns][xs].v;
            col[xs] = 1, col[xns] = 0;
            slackv[xs] = 0, calc_slackv(xns);
            q_push(xns);
        }
        col[xr] = 1;
        fa[xr] = fa[b];
        for (int i = pr + 1; i < size(bloch[b]); i++)
        {
            int xs = bloch[b][i];
            col[xs] = -1;
            calc_slackv(xs);
        }

        bel[b] = 0;
    }
    inline void expand_blossom_final(int b) // at the final stage
    {
        for (int i = 0; i < size(bloch[b]); i++)
        {
            if (bloch[b][i] > n && lab[bloch[b][i]] == 0)
                expand_blossom_final(bloch[b][i]);
            else
                set_bel(bloch[b][i], bloch[b][i]);
        }
        bel[b] = 0;
    }

    inline bool on_found_edge(const edge &e)
    {
        int xv = bel[e.v], xu = bel[e.u];
        if (col[xu] == -1)
        {
            int nv = bel[mate[xu]];
            fa[xu] = e.v;
            col[xu] = 1, col[nv] = 0;
            slackv[xu] = slackv[nv] = 0;
            q_push(nv);
        }
        else if (col[xu] == 0)
        {
            int xa = get_lca(xv, xu);
            if (!xa)
            {
                augment(xv, xu), augment(xu, xv);
                for (int b = n + 1; b <= n_x; b++)
                    if (bel[b] == b && lab[b] == 0)
                        expand_blossom_final(b);
                return true;
            }
            else
                add_blossom(xv, xa, xu);
        }
        return false;
    }

    bool match()
    {
        for (int x = 1; x <= n_x; x++)
            col[x] = -1, slackv[x] = 0;

        q_n = 0;
        for (int x = 1; x <= n_x; x++)
            if (bel[x] == x && !mate[x])
                fa[x] = 0, col[x] = 0, slackv[x] = 0, q_push(x);
        if (q_n == 0)
            return false;

        while (true)
        {
            for (int i = 0; i < q_n; i++)
            {
                int v = q[i];
                for (int u = 1; u <= n; u++)
                    if (mat[v][u].w > 0 && bel[v] != bel[u])
                    {
                        int d = e_delta(mat[v][u]);
                        if (d == 0)
                        {
                            if (on_found_edge(mat[v][u]))
                                return true;
                        }
                        else if (col[bel[u]] == -1 || col[bel[u]] == 0)
                            update_slackv(v, bel[u]);
                    }
            }

            int d = INF;
            for (int v = 1; v <= n; v++)
                if (col[bel[v]] == 0)
                    tension(d, lab[v]);
            for (int b = n + 1; b <= n_x; b++)
                if (bel[b] == b && col[b] == 1)
                    tension(d, lab[b] / 2);
            for (int x = 1; x <= n_x; x++)
                if (bel[x] == x && slackv[x])
                {
                    if (col[x] == -1)
                        tension(d, e_delta(mat[slackv[x]][x]));
                    else if (col[x] == 0)
                        tension(d, e_delta(mat[slackv[x]][x]) / 2);
                }

            for (int v = 1; v <= n; v++)
            {
                if (col[bel[v]] == 0)
                    lab[v] -= d;
                else if (col[bel[v]] == 1)
                    lab[v] += d;
            }
            for (int b = n + 1; b <= n_x; b++)
                if (bel[b] == b)
                {
                    if (col[bel[b]] == 0)
                        lab[b] += d * 2;
                    else if (col[bel[b]] == 1)
                        lab[b] -= d * 2;
                }

            q_n = 0;
            for (int v = 1; v <= n; v++)
                if (lab[v] == 0) // all unmatched vertices' labels are zero! cheers!
                    return false;
            for (int x = 1; x <= n_x; x++)
                if (bel[x] == x && slackv[x] && bel[slackv[x]] != x && e_delta(mat[slackv[x]][x]) == 0)
                {
                    if (on_found_edge(mat[slackv[x]][x]))
                        return true;
                }
            for (int b = n + 1; b <= n_x; b++)
                if (bel[b] == b && col[b] == 1 && lab[b] == 0)
                    expand_blossom1(b);
        }
        return false;
    }


    void init(int n)
    {
        this->n=n;
        for (int v = 1; v <= n; v++)
            for (int u = 1; u <= n; u++)
                mat[v][u] = edge(v, u, 0);
    }

    void addedge(int u,int v,int w)
    {
        mat[u][v].w=mat[v][u].w=w;
    }

    void calc_max_weight_match()
    {
        for (int v = 1; v <= n; v++)
            mate[v] = 0;

        n_x = n;
        n_matches = 0;
        tot_weight = 0;

        bel[0] = 0;
        for (int v = 1; v <= n; v++)
            bel[v] = v, bloch[v].clear();
        for (int v = 1; v <= n; v++)
            for (int u = 1; u <= n; u++)
                blofrom[v][u] = v == u ? v : 0;

        int w_max = 0;
        for (int v = 1; v <= n; v++)
            for (int u = 1; u <= n; u++)
                relax(w_max, mat[v][u].w);
        for (int v = 1; v <= n; v++)
            lab[v] = w_max;

        while (match())
            n_matches++;

        for (int v = 1; v <= n; v++)
            if (mate[v] && mate[v] < v)
                tot_weight += mat[v][mate[v]].w;
    }
}BA;

int main()
{
    BA.init(n);//初始化
    BA.addedge(u,v,w)//加边操作
    BA.calc_max_weight_match();//匹配
    printf("%lld\n", BA.tot_weight);//输出最大匹配
    for (int v = 1; v <= BA.n; v++)
        printf("%d ",BA.mate[v]);//输出另一个匹配点
    return 0;
}

 

posted @ 2019-08-26 11:57  1371767389  阅读(323)  评论(0编辑  收藏  举报