Skip to content

global_min_cut.hpp

SECTIONGraph INCLUDEnoya/global_min_cut.hpp

Stoer-Wagner global minimum cut of an undirected weighted graph in O(n^3), returning (cut weight, one side of the cut).

求无向带权图不指定端点的全局最小割,并恢复割的一个侧集。

Implementation

View on GitHub

#ifndef NOYA_GLOBAL_MIN_CUT_HPP
#define NOYA_GLOBAL_MIN_CUT_HPP 1

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

#include <algorithm>
#include <cassert>
#include <limits>
#include <utility>
#include <vector>

namespace noya {

/// @brief Stoer-Wagner global minimum cut of an undirected weighted graph in
/// O(n^3), returning (cut weight, one side of the cut).
template <class T>
std::pair<T, std::vector<int>>
global_min_cut(std::vector<std::vector<T>> weight) {
  int n = int(weight.size());
  for (int i = 0; i < n; i++) {
    assert(int(weight[i].size()) == n);
    assert(weight[i][i] == T{});
    for (int j = 0; j < n; j++) {
      assert(weight[i][j] == weight[j][i] && !(weight[i][j] < T{}));
    }
  }
  if (n <= 1) {
    return {T{}, n == 0 ? std::vector<int>{} : std::vector<int>{0}};
  }

  std::vector<bool> active(n, true);
  std::vector<std::vector<int>> group(n);
  for (int i = 0; i < n; i++) {
    group[i] = {i};
  }
  T best = std::numeric_limits<T>::max();
  std::vector<int> best_side;
  for (int phase_size = n; phase_size > 1; phase_size--) {
    std::vector<T> connection(n);
    std::vector<bool> added(n);
    int previous = -1;
    for (int step = 0; step < phase_size; step++) {
      int selected = -1;
      for (int vertex = 0; vertex < n; vertex++) {
        if (active[vertex] && !added[vertex] &&
            (selected == -1 || connection[selected] < connection[vertex])) {
          selected = vertex;
        }
      }
      assert(selected != -1);
      if (step + 1 == phase_size) {
        if (connection[selected] < best) {
          best = connection[selected];
          best_side = group[selected];
        }
        for (int vertex = 0; vertex < n; vertex++) {
          if (active[vertex] && vertex != previous) {
            weight[previous][vertex] += weight[selected][vertex];
            weight[vertex][previous] = weight[previous][vertex];
          }
        }
        group[previous].insert(group[previous].end(), group[selected].begin(),
                               group[selected].end());
        active[selected] = false;
        break;
      }
      added[selected] = true;
      previous = selected;
      for (int vertex = 0; vertex < n; vertex++) {
        if (active[vertex] && !added[vertex]) {
          connection[vertex] += weight[selected][vertex];
        }
      }
    }
  }
  std::sort(best_side.begin(), best_side.end());
  return {best, best_side};
}

} // namespace noya

#endif // NOYA_GLOBAL_MIN_CUT_HPP
#include <algorithm>
#include <cassert>
#include <limits>
#include <utility>
#include <vector>

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

namespace noya {

/// @brief Stoer-Wagner global minimum cut of an undirected weighted graph in
/// O(n^3), returning (cut weight, one side of the cut).
template <class T>
std::pair<T, std::vector<int>>
global_min_cut(std::vector<std::vector<T>> weight) {
  int n = int(weight.size());
  for (int i = 0; i < n; i++) {
    assert(int(weight[i].size()) == n);
    assert(weight[i][i] == T{});
    for (int j = 0; j < n; j++) {
      assert(weight[i][j] == weight[j][i] && !(weight[i][j] < T{}));
    }
  }
  if (n <= 1) {
    return {T{}, n == 0 ? std::vector<int>{} : std::vector<int>{0}};
  }

  std::vector<bool> active(n, true);
  std::vector<std::vector<int>> group(n);
  for (int i = 0; i < n; i++) {
    group[i] = {i};
  }
  T best = std::numeric_limits<T>::max();
  std::vector<int> best_side;
  for (int phase_size = n; phase_size > 1; phase_size--) {
    std::vector<T> connection(n);
    std::vector<bool> added(n);
    int previous = -1;
    for (int step = 0; step < phase_size; step++) {
      int selected = -1;
      for (int vertex = 0; vertex < n; vertex++) {
        if (active[vertex] && !added[vertex] &&
            (selected == -1 || connection[selected] < connection[vertex])) {
          selected = vertex;
        }
      }
      assert(selected != -1);
      if (step + 1 == phase_size) {
        if (connection[selected] < best) {
          best = connection[selected];
          best_side = group[selected];
        }
        for (int vertex = 0; vertex < n; vertex++) {
          if (active[vertex] && vertex != previous) {
            weight[previous][vertex] += weight[selected][vertex];
            weight[vertex][previous] = weight[previous][vertex];
          }
        }
        group[previous].insert(group[previous].end(), group[selected].begin(),
                               group[selected].end());
        active[selected] = false;
        break;
      }
      added[selected] = true;
      previous = selected;
      for (int vertex = 0; vertex < n; vertex++) {
        if (active[vertex] && !added[vertex]) {
          connection[vertex] += weight[selected][vertex];
        }
      }
    }
  }
  std::sort(best_side.begin(), best_side.end());
  return {best, best_side};
}

} // namespace noya