一般图最大匹配
先说下思想:
一般图与二分图不同的地方就在于奇环, 考虑出现奇环如何处理, 我们发现我们从一个点搜到一个奇环时, 最先搜到的地方我们称为花基, 那么会发现从花基从逆时针, 顺时针搜到这个奇环中的某一个点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;
}