Skip to content

general_matching.hpp

SECTIONGraph INCLUDEnoya/general_matching.hpp

用带花树求一般无向图最大基数匹配;适合图不保证二分的配对问题。

Complexity: Time: O(V^3). Space: O(V^2).

AC 记录:general_matching

跳到代码 · GitHub ↗

Implementation

当前头文件,省略 include guard;依赖见 #include

/// @complexity Time: O(V^3).
/// Space: O(V^2).

#include <algorithm>
#include <cassert>
#include <numeric>
#include <queue>
#include <utility>
#include <vector>

namespace noya {

namespace general_matching_internal {

struct solver {
  int n;
  std::vector<std::vector<int>> g;
  std::vector<int> mat, fa, bas;
  std::vector<bool> vis, bl;

  solver(int n_, const std::vector<std::pair<int, int>> &es)
      : n(n_), g(n), mat(n, -1), fa(n), bas(n), vis(n), bl(n) {
    assert(n >= 0);
    for (auto [u, v] : es) {
      assert(0 <= u && u < n);
      assert(0 <= v && v < n);
      if (u != v) {
        g[u].push_back(v);
        g[v].push_back(u);
      }
    }
  }

  int least_common_ancestor(int a, int b) {
    std::vector<bool> on(n);
    while (true) {
      a = bas[a];
      on[a] = true;
      if (mat[a] == -1) {
        break;
      }
      a = fa[mat[a]];
    }
    while (true) {
      b = bas[b];
      if (on[b]) {
        return b;
      }
      b = fa[mat[b]];
    }
  }

  void mark_path(int x, int cb, int v1) {
    while (bas[x] != cb) {
      bl[bas[x]] = true;
      bl[bas[mat[x]]] = true;
      fa[x] = v1;
      v1 = mat[x];
      x = fa[mat[x]];
    }
  }

  bool augment_from(int rt) {
    std::fill(vis.begin(), vis.end(), false);
    std::fill(fa.begin(), fa.end(), -1);
    std::iota(bas.begin(), bas.end(), 0);

    std::queue<int> q;
    q.push(rt);
    vis[rt] = true;
    while (!q.empty()) {
      int x = q.front();
      q.pop();
      for (int to : g[x]) {
        if (bas[x] == bas[to] || mat[x] == to) {
          continue;
        }
        if (to == rt || (mat[to] != -1 && fa[mat[to]] != -1)) {
          int cb = least_common_ancestor(x, to);
          std::fill(bl.begin(), bl.end(), false);
          mark_path(x, cb, to);
          mark_path(to, cb, x);
          for (int i = 0; i < n; i++) {
            if (!bl[bas[i]]) {
              continue;
            }
            bas[i] = cb;
            if (!vis[i]) {
              vis[i] = true;
              q.push(i);
            }
          }
        } else if (fa[to] == -1) {
          fa[to] = x;
          if (mat[to] == -1) {
            for (int cur = to; cur != -1;) {
              int pre = fa[cur];
              int nxt = pre == -1 ? -1 : mat[pre];
              mat[cur] = pre;
              if (pre != -1) {
                mat[pre] = cur;
              }
              cur = nxt;
            }
            return true;
          }
          to = mat[to];
          vis[to] = true;
          q.push(to);
        }
      }
    }
    return false;
  }

  std::pair<int, std::vector<int>> run() {
    int siz = 0;
    for (int x = 0; x < n; x++) {
      if (mat[x] == -1 && augment_from(x)) {
        siz++;
      }
    }
    return {siz, mat};
  }
};

} // namespace general_matching_internal

/// @brief Maximum matching in a general undirected graph in O(n^3).
/// @return (matching size, mate of every vertex), with -1 for unmatched
/// vertices.
inline std::pair<int, std::vector<int>>
maximum_matching(int n, const std::vector<std::pair<int, int>> &es) {
  return general_matching_internal::solver(n, es).run();
}

} // namespace noya
#ifndef NOYA_GENERAL_MATCHING_HPP
#define NOYA_GENERAL_MATCHING_HPP 1

/// @complexity Time: O(V^3).
/// Space: O(V^2).

#include <algorithm>
#include <cassert>
#include <numeric>
#include <queue>
#include <utility>
#include <vector>

namespace noya {

namespace general_matching_internal {

struct solver {
  int n;
  std::vector<std::vector<int>> g;
  std::vector<int> mat, fa, bas;
  std::vector<bool> vis, bl;

  solver(int n_, const std::vector<std::pair<int, int>> &es)
      : n(n_), g(n), mat(n, -1), fa(n), bas(n), vis(n), bl(n) {
    assert(n >= 0);
    for (auto [u, v] : es) {
      assert(0 <= u && u < n);
      assert(0 <= v && v < n);
      if (u != v) {
        g[u].push_back(v);
        g[v].push_back(u);
      }
    }
  }

  int least_common_ancestor(int a, int b) {
    std::vector<bool> on(n);
    while (true) {
      a = bas[a];
      on[a] = true;
      if (mat[a] == -1) {
        break;
      }
      a = fa[mat[a]];
    }
    while (true) {
      b = bas[b];
      if (on[b]) {
        return b;
      }
      b = fa[mat[b]];
    }
  }

  void mark_path(int x, int cb, int v1) {
    while (bas[x] != cb) {
      bl[bas[x]] = true;
      bl[bas[mat[x]]] = true;
      fa[x] = v1;
      v1 = mat[x];
      x = fa[mat[x]];
    }
  }

  bool augment_from(int rt) {
    std::fill(vis.begin(), vis.end(), false);
    std::fill(fa.begin(), fa.end(), -1);
    std::iota(bas.begin(), bas.end(), 0);

    std::queue<int> q;
    q.push(rt);
    vis[rt] = true;
    while (!q.empty()) {
      int x = q.front();
      q.pop();
      for (int to : g[x]) {
        if (bas[x] == bas[to] || mat[x] == to) {
          continue;
        }
        if (to == rt || (mat[to] != -1 && fa[mat[to]] != -1)) {
          int cb = least_common_ancestor(x, to);
          std::fill(bl.begin(), bl.end(), false);
          mark_path(x, cb, to);
          mark_path(to, cb, x);
          for (int i = 0; i < n; i++) {
            if (!bl[bas[i]]) {
              continue;
            }
            bas[i] = cb;
            if (!vis[i]) {
              vis[i] = true;
              q.push(i);
            }
          }
        } else if (fa[to] == -1) {
          fa[to] = x;
          if (mat[to] == -1) {
            for (int cur = to; cur != -1;) {
              int pre = fa[cur];
              int nxt = pre == -1 ? -1 : mat[pre];
              mat[cur] = pre;
              if (pre != -1) {
                mat[pre] = cur;
              }
              cur = nxt;
            }
            return true;
          }
          to = mat[to];
          vis[to] = true;
          q.push(to);
        }
      }
    }
    return false;
  }

  std::pair<int, std::vector<int>> run() {
    int siz = 0;
    for (int x = 0; x < n; x++) {
      if (mat[x] == -1 && augment_from(x)) {
        siz++;
      }
    }
    return {siz, mat};
  }
};

} // namespace general_matching_internal

/// @brief Maximum matching in a general undirected graph in O(n^3).
/// @return (matching size, mate of every vertex), with -1 for unmatched
/// vertices.
inline std::pair<int, std::vector<int>>
maximum_matching(int n, const std::vector<std::pair<int, int>> &es) {
  return general_matching_internal::solver(n, es).run();
}

} // namespace noya

#endif // NOYA_GENERAL_MATCHING_HPP
#include <algorithm>
#include <cassert>
#include <numeric>
#include <queue>
#include <utility>
#include <vector>

/// @complexity Time: O(V^3).
/// Space: O(V^2).

namespace noya {

namespace general_matching_internal {

struct solver {
  int n;
  std::vector<std::vector<int>> g;
  std::vector<int> mat, fa, bas;
  std::vector<bool> vis, bl;

  solver(int n_, const std::vector<std::pair<int, int>> &es)
      : n(n_), g(n), mat(n, -1), fa(n), bas(n), vis(n), bl(n) {
    assert(n >= 0);
    for (auto [u, v] : es) {
      assert(0 <= u && u < n);
      assert(0 <= v && v < n);
      if (u != v) {
        g[u].push_back(v);
        g[v].push_back(u);
      }
    }
  }

  int least_common_ancestor(int a, int b) {
    std::vector<bool> on(n);
    while (true) {
      a = bas[a];
      on[a] = true;
      if (mat[a] == -1) {
        break;
      }
      a = fa[mat[a]];
    }
    while (true) {
      b = bas[b];
      if (on[b]) {
        return b;
      }
      b = fa[mat[b]];
    }
  }

  void mark_path(int x, int cb, int v1) {
    while (bas[x] != cb) {
      bl[bas[x]] = true;
      bl[bas[mat[x]]] = true;
      fa[x] = v1;
      v1 = mat[x];
      x = fa[mat[x]];
    }
  }

  bool augment_from(int rt) {
    std::fill(vis.begin(), vis.end(), false);
    std::fill(fa.begin(), fa.end(), -1);
    std::iota(bas.begin(), bas.end(), 0);

    std::queue<int> q;
    q.push(rt);
    vis[rt] = true;
    while (!q.empty()) {
      int x = q.front();
      q.pop();
      for (int to : g[x]) {
        if (bas[x] == bas[to] || mat[x] == to) {
          continue;
        }
        if (to == rt || (mat[to] != -1 && fa[mat[to]] != -1)) {
          int cb = least_common_ancestor(x, to);
          std::fill(bl.begin(), bl.end(), false);
          mark_path(x, cb, to);
          mark_path(to, cb, x);
          for (int i = 0; i < n; i++) {
            if (!bl[bas[i]]) {
              continue;
            }
            bas[i] = cb;
            if (!vis[i]) {
              vis[i] = true;
              q.push(i);
            }
          }
        } else if (fa[to] == -1) {
          fa[to] = x;
          if (mat[to] == -1) {
            for (int cur = to; cur != -1;) {
              int pre = fa[cur];
              int nxt = pre == -1 ? -1 : mat[pre];
              mat[cur] = pre;
              if (pre != -1) {
                mat[pre] = cur;
              }
              cur = nxt;
            }
            return true;
          }
          to = mat[to];
          vis[to] = true;
          q.push(to);
        }
      }
    }
    return false;
  }

  std::pair<int, std::vector<int>> run() {
    int siz = 0;
    for (int x = 0; x < n; x++) {
      if (mat[x] == -1 && augment_from(x)) {
        siz++;
      }
    }
    return {siz, mat};
  }
};

} // namespace general_matching_internal

/// @brief Maximum matching in a general undirected graph in O(n^3).
/// @return (matching size, mate of every vertex), with -1 for unmatched
/// vertices.
inline std::pair<int, std::vector<int>>
maximum_matching(int n, const std::vector<std::pair<int, int>> &es) {
  return general_matching_internal::solver(n, es).run();
}

} // namespace noya