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[] 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[] 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[] 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]; } }