Skip to content

eulerian_trail.hpp

SECTIONGraph INCLUDEnoya/eulerian_trail.hpp

判断有向/无向多重图是否存在欧拉迹或欧拉回路,并构造一条合法路径。

Complexity: Time: O(V + E). Space: O(V + E).

AC 记录:eulerian_trail_directed, eulerian_trail_undirected

跳到代码 · GitHub ↗

Implementation

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

/// @complexity Time: O(V + E).
/// Space: O(V + E).

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

namespace noya {

/// @brief Vertex sequence and input edge ids of an Eulerian trail.
struct eulerian_trail_result {
  std::vector<int> vs;
  std::vector<int> eid;
};

/// @brief Construct a directed Eulerian trail using every input edge once, or
/// return nullopt when none exists. Set start to -1 to choose it automatically.
inline std::optional<eulerian_trail_result>
directed_eulerian_trail(int n, const std::vector<std::pair<int, int>> &es,
                        int s = -1) {
  assert(n >= 0);
  assert(-1 <= s && s < n);
  std::vector<std::vector<int>> g(n);
  std::vector<int> deg(n), dou(n);
  for (int ei1 = 0; ei1 < int(es.size()); ei1++) {
    auto [u, v] = es[ei1];
    assert(0 <= u && u < n);
    assert(0 <= v && v < n);
    g[u].push_back(ei1);
    dou[u]++;
    deg[v]++;
  }
  if (es.empty()) {
    eulerian_trail_result res;
    if (n > 0) {
      res.vs.push_back(s == -1 ? 0 : s);
    }
    return res;
  }

  int st = -1;
  int en = -1;
  for (int x = 0; x < n; x++) {
    int dif = dou[x] - deg[x];
    if (dif == 1) {
      if (st != -1) {
        return std::nullopt;
      }
      st = x;
    } else if (dif == -1) {
      if (en != -1) {
        return std::nullopt;
      }
      en = x;
    } else if (dif != 0) {
      return std::nullopt;
    }
  }
  if ((st == -1) != (en == -1)) {
    return std::nullopt;
  }

  if (s != -1) {
    if ((st != -1 && s != st) || (st == -1 && dou[s] == 0)) {
      return std::nullopt;
    }
  } else if (st != -1) {
    s = st;
  } else {
    s = int(
        std::find_if(dou.begin(), dou.end(), [](int de1) { return de1 > 0; }) -
        dou.begin());
  }

  std::vector<int> off(n);
  std::vector<int> vs1 = {s};
  std::vector<int> es1;
  eulerian_trail_result res;
  while (!vs1.empty()) {
    int x = vs1.back();
    if (off[x] < int(g[x].size())) {
      int ei1 = g[x][off[x]++];
      vs1.push_back(es[ei1].second);
      es1.push_back(ei1);
    } else {
      res.vs.push_back(x);
      vs1.pop_back();
      if (!es1.empty()) {
        res.eid.push_back(es1.back());
        es1.pop_back();
      }
    }
  }
  if (res.eid.size() != es.size()) {
    return std::nullopt;
  }
  std::reverse(res.vs.begin(), res.vs.end());
  std::reverse(res.eid.begin(), res.eid.end());
  return res;
}

/// @brief Construct an undirected Eulerian trail using every input edge once,
/// or return nullopt when none exists. Parallel edges and self-loops are
/// supported; set start to -1 to choose it automatically.
inline std::optional<eulerian_trail_result>
undirected_eulerian_trail(int n, const std::vector<std::pair<int, int>> &es,
                          int s = -1) {
  assert(n >= 0);
  assert(-1 <= s && s < n);
  std::vector<std::vector<std::pair<int, int>>> g(n);
  std::vector<int> de1(n);
  for (int ei1 = 0; ei1 < int(es.size()); ei1++) {
    auto [u, v] = es[ei1];
    assert(0 <= u && u < n);
    assert(0 <= v && v < n);
    g[u].emplace_back(v, ei1);
    g[v].emplace_back(u, ei1);
    de1[u]++;
    de1[v]++;
  }
  if (es.empty()) {
    eulerian_trail_result res;
    if (n > 0) {
      res.vs.push_back(s == -1 ? 0 : s);
    }
    return res;
  }

  std::vector<int> odd;
  for (int x = 0; x < n; x++) {
    if (de1[x] & 1) {
      odd.push_back(x);
    }
  }
  if (!odd.empty() && odd.size() != 2) {
    return std::nullopt;
  }
  if (s != -1) {
    if ((!odd.empty() && s != odd[0] && s != odd[1]) ||
        (odd.empty() && de1[s] == 0)) {
      return std::nullopt;
    }
  } else if (!odd.empty()) {
    s = odd[0];
  } else {
    s = int(
        std::find_if(de1.begin(), de1.end(), [](int val) { return val > 0; }) -
        de1.begin());
  }

  std::vector<int> off(n);
  std::vector<bool> vis(es.size());
  std::vector<int> vs1 = {s};
  std::vector<int> es1;
  eulerian_trail_result res;
  while (!vs1.empty()) {
    int x = vs1.back();
    while (off[x] < int(g[x].size()) && vis[g[x][off[x]].second]) {
      off[x]++;
    }
    if (off[x] < int(g[x].size())) {
      auto [nxt, ei1] = g[x][off[x]++];
      vis[ei1] = true;
      vs1.push_back(nxt);
      es1.push_back(ei1);
    } else {
      res.vs.push_back(x);
      vs1.pop_back();
      if (!es1.empty()) {
        res.eid.push_back(es1.back());
        es1.pop_back();
      }
    }
  }
  if (res.eid.size() != es.size()) {
    return std::nullopt;
  }
  std::reverse(res.vs.begin(), res.vs.end());
  std::reverse(res.eid.begin(), res.eid.end());
  return res;
}

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

/// @complexity Time: O(V + E).
/// Space: O(V + E).

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

namespace noya {

/// @brief Vertex sequence and input edge ids of an Eulerian trail.
struct eulerian_trail_result {
  std::vector<int> vs;
  std::vector<int> eid;
};

/// @brief Construct a directed Eulerian trail using every input edge once, or
/// return nullopt when none exists. Set start to -1 to choose it automatically.
inline std::optional<eulerian_trail_result>
directed_eulerian_trail(int n, const std::vector<std::pair<int, int>> &es,
                        int s = -1) {
  assert(n >= 0);
  assert(-1 <= s && s < n);
  std::vector<std::vector<int>> g(n);
  std::vector<int> deg(n), dou(n);
  for (int ei1 = 0; ei1 < int(es.size()); ei1++) {
    auto [u, v] = es[ei1];
    assert(0 <= u && u < n);
    assert(0 <= v && v < n);
    g[u].push_back(ei1);
    dou[u]++;
    deg[v]++;
  }
  if (es.empty()) {
    eulerian_trail_result res;
    if (n > 0) {
      res.vs.push_back(s == -1 ? 0 : s);
    }
    return res;
  }

  int st = -1;
  int en = -1;
  for (int x = 0; x < n; x++) {
    int dif = dou[x] - deg[x];
    if (dif == 1) {
      if (st != -1) {
        return std::nullopt;
      }
      st = x;
    } else if (dif == -1) {
      if (en != -1) {
        return std::nullopt;
      }
      en = x;
    } else if (dif != 0) {
      return std::nullopt;
    }
  }
  if ((st == -1) != (en == -1)) {
    return std::nullopt;
  }

  if (s != -1) {
    if ((st != -1 && s != st) || (st == -1 && dou[s] == 0)) {
      return std::nullopt;
    }
  } else if (st != -1) {
    s = st;
  } else {
    s = int(
        std::find_if(dou.begin(), dou.end(), [](int de1) { return de1 > 0; }) -
        dou.begin());
  }

  std::vector<int> off(n);
  std::vector<int> vs1 = {s};
  std::vector<int> es1;
  eulerian_trail_result res;
  while (!vs1.empty()) {
    int x = vs1.back();
    if (off[x] < int(g[x].size())) {
      int ei1 = g[x][off[x]++];
      vs1.push_back(es[ei1].second);
      es1.push_back(ei1);
    } else {
      res.vs.push_back(x);
      vs1.pop_back();
      if (!es1.empty()) {
        res.eid.push_back(es1.back());
        es1.pop_back();
      }
    }
  }
  if (res.eid.size() != es.size()) {
    return std::nullopt;
  }
  std::reverse(res.vs.begin(), res.vs.end());
  std::reverse(res.eid.begin(), res.eid.end());
  return res;
}

/// @brief Construct an undirected Eulerian trail using every input edge once,
/// or return nullopt when none exists. Parallel edges and self-loops are
/// supported; set start to -1 to choose it automatically.
inline std::optional<eulerian_trail_result>
undirected_eulerian_trail(int n, const std::vector<std::pair<int, int>> &es,
                          int s = -1) {
  assert(n >= 0);
  assert(-1 <= s && s < n);
  std::vector<std::vector<std::pair<int, int>>> g(n);
  std::vector<int> de1(n);
  for (int ei1 = 0; ei1 < int(es.size()); ei1++) {
    auto [u, v] = es[ei1];
    assert(0 <= u && u < n);
    assert(0 <= v && v < n);
    g[u].emplace_back(v, ei1);
    g[v].emplace_back(u, ei1);
    de1[u]++;
    de1[v]++;
  }
  if (es.empty()) {
    eulerian_trail_result res;
    if (n > 0) {
      res.vs.push_back(s == -1 ? 0 : s);
    }
    return res;
  }

  std::vector<int> odd;
  for (int x = 0; x < n; x++) {
    if (de1[x] & 1) {
      odd.push_back(x);
    }
  }
  if (!odd.empty() && odd.size() != 2) {
    return std::nullopt;
  }
  if (s != -1) {
    if ((!odd.empty() && s != odd[0] && s != odd[1]) ||
        (odd.empty() && de1[s] == 0)) {
      return std::nullopt;
    }
  } else if (!odd.empty()) {
    s = odd[0];
  } else {
    s = int(
        std::find_if(de1.begin(), de1.end(), [](int val) { return val > 0; }) -
        de1.begin());
  }

  std::vector<int> off(n);
  std::vector<bool> vis(es.size());
  std::vector<int> vs1 = {s};
  std::vector<int> es1;
  eulerian_trail_result res;
  while (!vs1.empty()) {
    int x = vs1.back();
    while (off[x] < int(g[x].size()) && vis[g[x][off[x]].second]) {
      off[x]++;
    }
    if (off[x] < int(g[x].size())) {
      auto [nxt, ei1] = g[x][off[x]++];
      vis[ei1] = true;
      vs1.push_back(nxt);
      es1.push_back(ei1);
    } else {
      res.vs.push_back(x);
      vs1.pop_back();
      if (!es1.empty()) {
        res.eid.push_back(es1.back());
        es1.pop_back();
      }
    }
  }
  if (res.eid.size() != es.size()) {
    return std::nullopt;
  }
  std::reverse(res.vs.begin(), res.vs.end());
  std::reverse(res.eid.begin(), res.eid.end());
  return res;
}

} // namespace noya

#endif // NOYA_EULERIAN_TRAIL_HPP
#include <algorithm>
#include <cassert>
#include <optional>
#include <utility>
#include <vector>

/// @complexity Time: O(V + E).
/// Space: O(V + E).

namespace noya {

/// @brief Vertex sequence and input edge ids of an Eulerian trail.
struct eulerian_trail_result {
  std::vector<int> vs;
  std::vector<int> eid;
};

/// @brief Construct a directed Eulerian trail using every input edge once, or
/// return nullopt when none exists. Set start to -1 to choose it automatically.
inline std::optional<eulerian_trail_result>
directed_eulerian_trail(int n, const std::vector<std::pair<int, int>> &es,
                        int s = -1) {
  assert(n >= 0);
  assert(-1 <= s && s < n);
  std::vector<std::vector<int>> g(n);
  std::vector<int> deg(n), dou(n);
  for (int ei1 = 0; ei1 < int(es.size()); ei1++) {
    auto [u, v] = es[ei1];
    assert(0 <= u && u < n);
    assert(0 <= v && v < n);
    g[u].push_back(ei1);
    dou[u]++;
    deg[v]++;
  }
  if (es.empty()) {
    eulerian_trail_result res;
    if (n > 0) {
      res.vs.push_back(s == -1 ? 0 : s);
    }
    return res;
  }

  int st = -1;
  int en = -1;
  for (int x = 0; x < n; x++) {
    int dif = dou[x] - deg[x];
    if (dif == 1) {
      if (st != -1) {
        return std::nullopt;
      }
      st = x;
    } else if (dif == -1) {
      if (en != -1) {
        return std::nullopt;
      }
      en = x;
    } else if (dif != 0) {
      return std::nullopt;
    }
  }
  if ((st == -1) != (en == -1)) {
    return std::nullopt;
  }

  if (s != -1) {
    if ((st != -1 && s != st) || (st == -1 && dou[s] == 0)) {
      return std::nullopt;
    }
  } else if (st != -1) {
    s = st;
  } else {
    s = int(
        std::find_if(dou.begin(), dou.end(), [](int de1) { return de1 > 0; }) -
        dou.begin());
  }

  std::vector<int> off(n);
  std::vector<int> vs1 = {s};
  std::vector<int> es1;
  eulerian_trail_result res;
  while (!vs1.empty()) {
    int x = vs1.back();
    if (off[x] < int(g[x].size())) {
      int ei1 = g[x][off[x]++];
      vs1.push_back(es[ei1].second);
      es1.push_back(ei1);
    } else {
      res.vs.push_back(x);
      vs1.pop_back();
      if (!es1.empty()) {
        res.eid.push_back(es1.back());
        es1.pop_back();
      }
    }
  }
  if (res.eid.size() != es.size()) {
    return std::nullopt;
  }
  std::reverse(res.vs.begin(), res.vs.end());
  std::reverse(res.eid.begin(), res.eid.end());
  return res;
}

/// @brief Construct an undirected Eulerian trail using every input edge once,
/// or return nullopt when none exists. Parallel edges and self-loops are
/// supported; set start to -1 to choose it automatically.
inline std::optional<eulerian_trail_result>
undirected_eulerian_trail(int n, const std::vector<std::pair<int, int>> &es,
                          int s = -1) {
  assert(n >= 0);
  assert(-1 <= s && s < n);
  std::vector<std::vector<std::pair<int, int>>> g(n);
  std::vector<int> de1(n);
  for (int ei1 = 0; ei1 < int(es.size()); ei1++) {
    auto [u, v] = es[ei1];
    assert(0 <= u && u < n);
    assert(0 <= v && v < n);
    g[u].emplace_back(v, ei1);
    g[v].emplace_back(u, ei1);
    de1[u]++;
    de1[v]++;
  }
  if (es.empty()) {
    eulerian_trail_result res;
    if (n > 0) {
      res.vs.push_back(s == -1 ? 0 : s);
    }
    return res;
  }

  std::vector<int> odd;
  for (int x = 0; x < n; x++) {
    if (de1[x] & 1) {
      odd.push_back(x);
    }
  }
  if (!odd.empty() && odd.size() != 2) {
    return std::nullopt;
  }
  if (s != -1) {
    if ((!odd.empty() && s != odd[0] && s != odd[1]) ||
        (odd.empty() && de1[s] == 0)) {
      return std::nullopt;
    }
  } else if (!odd.empty()) {
    s = odd[0];
  } else {
    s = int(
        std::find_if(de1.begin(), de1.end(), [](int val) { return val > 0; }) -
        de1.begin());
  }

  std::vector<int> off(n);
  std::vector<bool> vis(es.size());
  std::vector<int> vs1 = {s};
  std::vector<int> es1;
  eulerian_trail_result res;
  while (!vs1.empty()) {
    int x = vs1.back();
    while (off[x] < int(g[x].size()) && vis[g[x][off[x]].second]) {
      off[x]++;
    }
    if (off[x] < int(g[x].size())) {
      auto [nxt, ei1] = g[x][off[x]++];
      vis[ei1] = true;
      vs1.push_back(nxt);
      es1.push_back(ei1);
    } else {
      res.vs.push_back(x);
      vs1.pop_back();
      if (!es1.empty()) {
        res.eid.push_back(es1.back());
        es1.pop_back();
      }
    }
  }
  if (res.eid.size() != es.size()) {
    return std::nullopt;
  }
  std::reverse(res.vs.begin(), res.vs.end());
  std::reverse(res.eid.begin(), res.eid.end());
  return res;
}

} // namespace noya