説明

グラフ $G=(V, E)$ において、 $V$ が $2$ つの部分集合 $X$ と $Y$ に分割され、 $E$ のどの辺も一方の端点は $X$ に、 もう一方の端点は $Y$ に属しているとき、 $G$ を二部グラフという。

グラフ $G=(V, E)$ において、 $M$ が $E$ の部分集合でかつ $M$ のどの $2$ 辺も共通の端点をもたないとき、 $M$ を $G$ のマッチングといい、 辺の本数が最大であるマッチングを最大マッチングという。

ここでは, 二部グラフの最大マッチングを最大流のアルゴリズムを利用して求める。

計算量

  • $O(V E)$

実装例

  • BipartiteMatching($n$):= 全体のグラフの頂点数を $n$ で初期化する。
  • add_edge($u$, $v$):= 頂点 $u, v$ 間に辺を張る。
  • bipartite_matching():= 二部グラフの最大マッチングを返す。
struct BipartiteMatching {
  vector< vector< int > > graph;
  vector< int > match, alive, used;
  int timestamp;

  BipartiteMatching(int n) : graph(n), alive(n, 1), used(n, 0), match(n, -1), timestamp(0) {}

  void add_edge(int u, int v) {
    graph[u].push_back(v);
    graph[v].push_back(u);
  }

  bool dfs(int idx) {
    used[idx] = timestamp;
    for(auto &to : graph[idx]) {
      int to_match = match[to];
      if(alive[to] == 0) continue;
      if(to_match == -1 || (used[to_match] != timestamp && dfs(to_match))) {
        match[idx] = to;
        match[to] = idx;
        return true;
      }
    }
    return false;
  }

  int bipartite_matching() {
    int ret = 0;
    for(int i = 0; i < graph.size(); i++) {
      if(alive[i] == 0) continue;
      if(match[i] == -1) {
        ++timestamp;
        ret += dfs(i);
      }
    }
    return ret;
  }

  void output() {
    for(int i = 0; i < graph.size(); i++) {
      if(i < match[i]) {
        cout << i << "-" << match[i] << endl;
      }
    }
  }
};

検証

AOJ GRL_7_A 2部マッチング

#define PROBLEM "http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=GRL_7_A"

#include "../../template/template.cpp"
#include "../template.cpp"

#include "../bipartite-matching.cpp"

int main() {
  int X, Y, E;
  scanf("%d %d %d", &X, &Y, &E);
  BipartiteMatching bm(X + Y);
  for(int i = 0; i < E; i++) {
    int a, b;
    scanf("%d %d", &a, &b);
    bm.add_edge(a, X + b);
  }
  printf("%d\n", bm.bipartite_matching());
}