模板题,也没有卡各种东西:

题目描述
给出一张 n 个点 m 条边的无向图,求该图的最大匹配。

输入格式
第一行两个正整数 n 和 m,分别表示图的点数和边数。

接下来 m 行,每行两个正整数 u 和 v,表示图中存在一条连接 u 和 v 的无向边。

输出格式
第一行一个整数,表示最大匹配数。

第二行 n 个整数,第 i 个数表示与结点 i 匹配的结点编号,若该结点无匹配则输出 0。

如有多解输出任意解即可。
n <= 1e3 , m<=5e4

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
#include<map>
#include<vector>
#define ll long long
#define llu unsigned ll
using namespace std;
const int maxn = 2100;
const int maxm = 101000;
const int inf = 0x3f3f3f3f;
int head[maxn], ver[maxm], nt[maxm];
int ha[maxn], match[maxn], pre[maxn], f[maxn], id[maxn];
//vis[i]: 0(未染色) 1(黑色) 2(白色)
//match[i]: i的匹配点
//f[i]: i在带花树中的祖先
//pre[i]: i的非匹配边的另一点
//id: 找LCA用
int n, m, tot, idx, ans;
queue<int>q;

void add(int x, int y)
{
    ver[++tot] = y, nt[tot] = head[x], head[x] = tot;
}

int fi(int x)
{
    if (f[x] != x)
        f[x] = fi(f[x]);
    return f[x];
}

void init(void)
{
    idx = ans = tot = 0;
    memset(head, 0, sizeof(head));
    memset(id, 0, sizeof(id));
    memset(match, 0, sizeof(match));
}

//查询x和y在带花树中的LCA
int LCA(int x, int y)
{
    //沿着增广路向上找lca ,x,y交替向上
    for (idx++, x = fi(x), y = fi(y);;swap(x, y))
    {
        //有可能环中有环(花中有花),所以用并查集找祖先,只处理祖先节点
        if (x)
        {
            if (id[x] == idx) return x;//x,y在同一环中,一定会找到已被编号的点,该点即为LCA。
            id[x] = idx, x = fi(pre[match[x]]);//给点编号,并沿着非匹配边向上找
        }
    }
}

//缩点(开花),将x、y到LCA(l)路径中的点,缩为一点
void blossom(int x, int y, int lca)
{
    while (fi(x) != lca)
    {
        //增广路取反
        pre[x] = y;y = match[x];

        //如果x、y的奇环中有白点,将其染为黑点,放入队列,让其去找不是环中的匹配点
        if (ha[y] == 2) ha[y] = 1, q.push(y);

        //只改变是根的点
        if (fi(x) == x) f[x] = lca;
        if (fi(y) == y) f[y] = lca;
        x = pre[y];
    }
}

int bfs(int s)
{
    //每次都以s为起点bfs,建带花树

    for (int i = 1;i <= n;i++)
        ha[i] = pre[i] = 0, f[i] = i;
    while (q.size()) q.pop();
    q.push(s);
    ha[s] = 1;

    while (q.size())
    {
        int x = q.front();
        q.pop();

        for (int i = head[x];i;i = nt[i])
        {
            int y = ver[i];

            //如果已经在同一个环(花)中或者是白点(意为这已经有匹配点),直接跳过
            //这种情况不会增加匹配数
            if (fi(x) == fi(y) || ha[y] == 2) continue;

            //如果没有被染色
            if (!ha[y])
            {
                //先染为白色,将前继点指向x
                ha[y] = 2;pre[y] = x;

                //如果没有被匹配过,直接匹配成功
                if (!match[y])
                {
                    //增广路取反
                    for (int k = y, last;k;k = last)
                        last = match[pre[k]], match[k] = pre[k], match[pre[k]] = k;
                    return 1;
                }
                //如果被匹配过,则把匹配v的点染为黑色,放入队列中
                ha[match[y]] = 1, q.push(match[y]);
            }

            //v是黑色,形成奇环,则缩点(开花)
            else
            {
                int lca = LCA(x, y);
                blossom(x, y, lca), blossom(y, x, lca);
            }
        }
    }
    return 0;
}

int main(void)
{
    int x, y;
    scanf("%d%d", &n, &m);
    for (int i = 1;i <= m;i++)
    {
        scanf("%d%d", &x, &y);
        add(x, y), add(y, x);
    }
    for (int i = 1;i <= n;i++)
    {
        if (!match[i])
            ans += bfs(i);
    }

    printf("%d\n", ans);
    for (int i = 1;i <= n;i++)
    {
        if (i != 1) putchar(' ');
        printf("%d", match[i]);
    }
    putchar('\n');
    return 0;
}