带花树
一般图最大匹配问题
可以理解成高级版的匈牙利算法,在二分图匹配中,环都是偶环,所以可以将点分成两部分,不会冲突,但是一般图中会出现奇环,这是直接增广就变得不可行。
考虑在增广时遇到一个奇环是什么情况,环中至少有一个点,可以向外侧匹配,这时,我们把一个奇环缩成一个点,再跑匈牙利就行了,可以增广时,把环拆开,把每个点对应的匹配连上就行了
记录一个 \(lk\) 表示每个点匹配的是哪个点, 记录一个 \(pre\) 表示每个点被哪个点遍历到,在缩点的时候,把 \(pre\) 变成双向的,就行了
一般图最大匹配模板 \(code\)
#include<bits/stdc++.h>
using namespace std;
#define gc getchar
#define rep(i, a, b) for(int i = a; i <= b; ++i)
#define rg register
inline int read(){
rg char ch = gc();
rg int x = 0, f = 0;
while(!isdigit(ch)) f |= (ch == '-'), ch = gc();
while(isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48), ch = gc();
return f ? -x : x;
}
const int N = 1e3 + 5, M = 1e5 + 5;
int head[N], ver[M], nxt[M], tot;
inline void add(int x, int y){
ver[++tot] = y;
nxt[tot] = head[x];
head[x] = tot;
}
inline void adds(int x, int y){ add(x, y); add(y, x); }
int n, m;
int lk[N], pre[N], dfn[N], f[N];
int q[N], ql, qr, cl[N], cnt;
int find(int x){
return f[x] ? f[x] = find(f[x]) : x;
}
inline int lca(int x, int y){
++cnt;
while(true){
if(dfn[x = find(x)] == cnt) return x;
if(x) dfn[x] = cnt; x = pre[lk[x]]; swap(x, y);
}
}
inline void flw(int x, int y, int p){
while(find(x) ^ p){
pre[x] = y; y = lk[x];
if(cl[y] == 2) cl[y] = 1, q[++qr] = y;
f[x] = f[y] = p; x = pre[y];
}
}
inline bool dfs(int x){
memset(f, 0, sizeof f);
memset(cl, 0, sizeof cl);
memset(pre, 0, sizeof pre);
cl[q[ql = qr = 1] = x] = 1;
for(int x = q[ql]; ql <= qr; x = q[++ql]){
for(int y, z, i = head[x]; i; i = nxt[i]){
if(cl[y = ver[i]] == 2) continue;
if(find(y) == find(x)) continue;
if(cl[y] == 1) z = lca(x, y), flw(x, y, z), flw(y, x, z);
else{
cl[y] = 2; pre[y] = x;
if(!lk[y]){
for(int u = y, v, lst; u; u = lst)
lst = lk[v = pre[u]], lk[u] = v, lk[v] = u;
return true;
}
cl[lk[y]] = 1; q[++qr] = lk[y];
}
}
}
return false;
}
signed main(){
n = read(), m = read();
rep(i, 1, m) adds(read(), read());
int ans = 0;
rep(i, 1, n) ans += (!lk[i] && dfs(i));
printf("%d\n", ans);
rep(i, 1, n) printf("%d ", lk[i]);
gc(), gc();
return 0;
}