A Systematic Approach to Algorithms

Vijay K. Garg · The University of Texas at Austin

Chapter 12. Bipartite Matching

Classical bipartite matching, vertex-cover, and chain-cover constructions.

This page: Classical forms. View LLP forms »

This page collects the classical / sequential implementations of the algorithms developed in this chapter. The lattice-linear (LLP) reformulations and chapter setup live on the LLP companion page.

BipartiteMatching

The classical sequential augmenting-path algorithm. tryMatch(u, ...) tries to match the left vertex $u$ via a DFS over the right side; if it succeeds, the path-toggling is folded into the recursion.

Time complexity: $O(n \cdot m)$ via the augmenting-path scheme, where $n$ is the number of left vertices and $m$ the number of edges.

int[] BipartiteMatching(int[][] adj) {
  int n = adj.length;
  int m = adj[0].length;
  int[] G       = new int[n];
  int[] partner = new int[m];
  int j = 0;
  while (j < m) {
    partner[j] = 0 - 1;
    j = j + 1;
  };
  int u = 0;
  while (u < n) {
    boolean[] seen = new boolean[m];
    if (tryMatch(u, adj, partner, seen)) {
      G[u] = 1;
    };
    u = u + 1;
  };
  return G;
}

boolean tryMatch(int u, int[][] adj, int[] partner, boolean[] seen) {
  int m = adj[0].length;
  int v = 0;
  while (v < m) {
    if (adj[u][v] == 1 && !seen[v]) {
      seen[v] = true;
      if (partner[v] == 0 - 1 || tryMatch(partner[v], adj, partner, seen)) {
        partner[v] = u;
        return true;
      }
    };
    v = v + 1;
  };
  return false;
}

ChainCoverFromMatching

Fulkerson's reduction in code: from a matching $M$ in the strict split of a poset, glue chains together by setting $C[u] := v$ for every matched pair $(u^-, v^+)$. Pointer-jumping then collapses each chain into a single root in $O(\log n)$ rounds.

Time complexity: $O(n)$ work, $O(\log n)$ parallel rounds via pointer-jumping.

int[] ParChainCoverFromMatching(int[] matchPartner) {
  int n = matchPartner.length;
  int[] C = new int[n];
  int i = 0;
  while (i < n) { C[i] = i; i = i + 1; };
  int u = 0;
  while (u < n) {
    int v = matchPartner[u];
    if (v != 0 - 1) {
      C[u] = v;
    };
    u = u + 1;
  };
  parentPointerJumping(C);
  return C;
}

void parentPointerJumping(int[] C) {
  int n = C.length;
  boolean changed = true;
  while (changed) {
    changed = false;
    int i = 0;
    while (i < n) {
      int p = C[i];
      if (C[p] != p) { C[i] = C[p]; changed = true; };
      i = i + 1;
    }
  }
}

VertexCoverFromMatching

King's-theorem construction in code: build a vertex cover of size $|M|$ in two parallel passes over the edges. Pass 1 puts every $L$-endpoint of $M$ into the cover. Pass 2 sweeps every uncovered edge $(u, v)$ with $u, v \notin C$, swapping the cover mark from a matched neighbour onto the uncovered endpoint.

Time complexity: $O(n + m)$.

boolean[] ParVertexCoverFromMatching(int[][] adj, int[] matchL) {
  int L = adj.length;
  int R = adj[0].length;
  boolean[] C       = new boolean[L + R];
  int[]     partner = new int[L + R];
  int i = 0;
  while (i < L + R) {
    partner[i] = 0 - 1;
    i = i + 1;
  };
  int u = 0;
  while (u < L) {
    int v = matchL[u];
    if (v != 0 - 1) {
      C[u]            = true;
      partner[u]      = L + v;
      partner[L + v]  = u;
    };
    u = u + 1;
  };
  u = 0;
  while (u < L) {
    int v = 0;
    while (v < R) {
      if (adj[u][v] == 1 && !C[u] && !C[L + v]) {
        if (partner[u] != 0 - 1) {
          C[partner[u]] = false;
          C[u]          = true;
        } else {
          C[partner[L + v]] = false;
          C[L + v]          = true;
        }
      };
      v = v + 1;
    };
    u = u + 1;
  };
  return C;
}