library

This documentation is automatically generated by online-judge-tools/verification-helper

View the Project on GitHub sash2104/library

:heavy_check_mark: ハンガリアン法
(math/hungarian.hpp)

Verified with

Code

/**
 * @title ハンガリアン法
 * @brief 二部グラフの最小重み最大マッチングを返す
 * 行数N, 列数Mとすると、N <= Mを満たす必要がある
 * 計算量 $O(N^2M)$
 * 元:https://ei1333.github.io/luzhiled/snippets/graph/hungarian.html
 */
#include <cassert>
#include <vector>
#include <limits>

template< typename T >
struct Hungarian {
  const T INF;
  const int N, M;
  std::vector<std::vector<T>> A; // 1-indexed
  std::vector<int> P, way, match; // match[i]: = 行iの割当先の列番号. 0-indexed
  std::vector< T > U, V, minV;
  std::vector<bool> used;
  Hungarian(const std::vector<std::vector<T>> &A_):
    INF(std::numeric_limits<T>::max()), N(A_.size()+1), M(A_[0].size()+1), A(N, std::vector<T>(M)), P(M), way(M), match(N-1),
    U(N, 0), V(M, 0), minV(M), used(M) {
      for(int i = 0; i < N-1; ++i) {
        for (int j = 0; j < M-1; ++j) {
          A[i+1][j+1] = A_[i][j];
        }
      }
  }
  int get(int i) const {
    // 行iの割当先の列を返す
    return match[i];
  }
  T solve() {
    for(int i = 1; i < N; i++) {
      P[0] = i;
      minV.assign(M, INF);
      used.assign(M, false);
      int j0 = 0;
      while(P[j0] != 0) {
        int i0 = P[j0], j1 = 0;
        used[j0] = true;
        T delta = INF;
        for(int j = 1; j < M; j++) {
          if(used[j]) continue;
          T curr = A[i0][j] - U[i0] - V[j];
          if(curr < minV[j]) minV[j] = curr, way[j] = j0;
          if(minV[j] < delta) delta = minV[j], j1 = j;
        }
        for(int j = 0; j < M; j++) {
          if(used[j]) U[P[j]] += delta, V[j] -= delta;
          else minV[j] -= delta;
        }
        j0 = j1;
      }
      do {
        P[j0] = P[way[j0]];
        j0 = way[j0];
      } while(j0 != 0);
    }
    for(int j = 1; j < M; j++) {
      if (P[j] == 0) continue;
      match[P[j]-1] = j-1;
    }
    return -V[0];
  }
};
#line 1 "math/hungarian.hpp"
/**
 * @title ハンガリアン法
 * @brief 二部グラフの最小重み最大マッチングを返す
 * 行数N, 列数Mとすると、N <= Mを満たす必要がある
 * 計算量 $O(N^2M)$
 * 元:https://ei1333.github.io/luzhiled/snippets/graph/hungarian.html
 */
#include <cassert>
#include <vector>
#include <limits>

template< typename T >
struct Hungarian {
  const T INF;
  const int N, M;
  std::vector<std::vector<T>> A; // 1-indexed
  std::vector<int> P, way, match; // match[i]: = 行iの割当先の列番号. 0-indexed
  std::vector< T > U, V, minV;
  std::vector<bool> used;
  Hungarian(const std::vector<std::vector<T>> &A_):
    INF(std::numeric_limits<T>::max()), N(A_.size()+1), M(A_[0].size()+1), A(N, std::vector<T>(M)), P(M), way(M), match(N-1),
    U(N, 0), V(M, 0), minV(M), used(M) {
      for(int i = 0; i < N-1; ++i) {
        for (int j = 0; j < M-1; ++j) {
          A[i+1][j+1] = A_[i][j];
        }
      }
  }
  int get(int i) const {
    // 行iの割当先の列を返す
    return match[i];
  }
  T solve() {
    for(int i = 1; i < N; i++) {
      P[0] = i;
      minV.assign(M, INF);
      used.assign(M, false);
      int j0 = 0;
      while(P[j0] != 0) {
        int i0 = P[j0], j1 = 0;
        used[j0] = true;
        T delta = INF;
        for(int j = 1; j < M; j++) {
          if(used[j]) continue;
          T curr = A[i0][j] - U[i0] - V[j];
          if(curr < minV[j]) minV[j] = curr, way[j] = j0;
          if(minV[j] < delta) delta = minV[j], j1 = j;
        }
        for(int j = 0; j < M; j++) {
          if(used[j]) U[P[j]] += delta, V[j] -= delta;
          else minV[j] -= delta;
        }
        j0 = j1;
      }
      do {
        P[j0] = P[way[j0]];
        j0 = way[j0];
      } while(j0 != 0);
    }
    for(int j = 1; j < M; j++) {
      if (P[j] == 0) continue;
      match[P[j]-1] = j-1;
    }
    return -V[0];
  }
};
Back to top page