模板题,也没有卡各种东西:
题目描述
给出一张 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;
}