128 lines
3.2 KiB
Java
128 lines
3.2 KiB
Java
import java.util.*;
|
|
import java.util.stream.*;
|
|
|
|
// https://en.wikipedia.org/wiki/Blossom_algorithm in O(V^3)
|
|
public class MaxMatchingEdmonds {
|
|
|
|
public static int maxMatching(List<Integer>[] graph) {
|
|
int n = graph.length;
|
|
int[] match = new int[n];
|
|
Arrays.fill(match, -1);
|
|
int[] p = new int[n];
|
|
for (int i = 0; i < n; ++i) {
|
|
if (match[i] == -1) {
|
|
int v = findPath(graph, match, p, i);
|
|
while (v != -1) {
|
|
int pv = p[v];
|
|
int ppv = match[pv];
|
|
match[v] = pv;
|
|
match[pv] = v;
|
|
v = ppv;
|
|
}
|
|
}
|
|
}
|
|
return (int) Arrays.stream(match).filter(x -> x != -1).count() / 2;
|
|
}
|
|
|
|
static int findPath(List<Integer>[] graph, int[] match, int[] p, int root) {
|
|
Arrays.fill(p, -1);
|
|
int n = graph.length;
|
|
int[] base = IntStream.range(0, n).toArray();
|
|
|
|
boolean[] used = new boolean[n];
|
|
int[] q = new int[n];
|
|
int qt = 0;
|
|
used[root] = true;
|
|
q[qt++] = root;
|
|
for (int qh = 0; qh < qt; qh++) {
|
|
int u = q[qh];
|
|
for (int v : graph[u]) {
|
|
if (base[u] == base[v] || match[u] == v) continue;
|
|
if (v == root || match[v] != -1 && p[match[v]] != -1) {
|
|
int curbase = lca(match, base, p, u, v);
|
|
boolean[] blossom = new boolean[n];
|
|
markPath(match, base, blossom, p, u, curbase, v);
|
|
markPath(match, base, blossom, p, v, curbase, u);
|
|
for (int i = 0; i < n; ++i)
|
|
if (blossom[base[i]]) {
|
|
base[i] = curbase;
|
|
if (!used[i]) {
|
|
used[i] = true;
|
|
q[qt++] = i;
|
|
}
|
|
}
|
|
} else if (p[v] == -1) {
|
|
p[v] = u;
|
|
if (match[v] == -1)
|
|
return v;
|
|
v = match[v];
|
|
used[v] = true;
|
|
q[qt++] = v;
|
|
}
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static void markPath(int[] match, int[] base, boolean[] blossom, int[] p, int u, int curbase, int child) {
|
|
for (; base[u] != curbase; u = p[match[u]]) {
|
|
blossom[base[u]] = blossom[base[match[u]]] = true;
|
|
p[u] = child;
|
|
child = match[u];
|
|
}
|
|
}
|
|
|
|
static int lca(int[] match, int[] base, int[] p, int a, int b) {
|
|
boolean[] used = new boolean[match.length];
|
|
while (true) {
|
|
a = base[a];
|
|
used[a] = true;
|
|
if (match[a] == -1) break;
|
|
a = p[match[a]];
|
|
}
|
|
while (true) {
|
|
b = base[b];
|
|
if (used[b]) return b;
|
|
b = p[match[b]];
|
|
}
|
|
}
|
|
|
|
// random test
|
|
public static void main(String[] args) {
|
|
Random rnd = new Random(1);
|
|
for (int step = 0; step < 1000; step++) {
|
|
int n = rnd.nextInt(10) + 1;
|
|
boolean[][] g = new boolean[n][n];
|
|
List<Integer>[] graph = Stream.generate(ArrayList::new).limit(n).toArray(List[]::new);
|
|
for (int i = 0; i < n; i++) {
|
|
for (int j = i + 1; j < n; j++) {
|
|
g[i][j] = g[j][i] = rnd.nextBoolean();
|
|
if (g[i][j]) {
|
|
graph[i].add(j);
|
|
graph[j].add(i);
|
|
}
|
|
}
|
|
}
|
|
int res1 = maxMatching(graph);
|
|
int res2 = maxMatchingSlow(g);
|
|
if (res1 != res2) {
|
|
System.err.println(res1 + " " + res2);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int maxMatchingSlow(boolean[][] g) {
|
|
int n = g.length;
|
|
int[] dp = new int[1 << n];
|
|
for (int mask = 0; mask < dp.length; mask++)
|
|
for (int i = 0; i < n; i++)
|
|
if ((mask & (1 << i)) == 0) {
|
|
for (int j = i + 1; j < n; j++)
|
|
if ((mask & (1 << j)) == 0 && g[i][j])
|
|
dp[mask | (1 << i) | (1 << j)] = Math.max(dp[mask | (1 << i) | (1 << j)], dp[mask] + 1);
|
|
break;
|
|
}
|
|
return dp[dp.length - 1];
|
|
}
|
|
}
|