Add README

This commit is contained in:
Triton 2026-09-15 00:25:10 +05:00
commit 4989641538
9 changed files with 859 additions and 0 deletions

37
DSU.h Normal file
View File

@ -0,0 +1,37 @@
#pragma once
#include <vector>
#include <utility>
struct DSU {
std::vector<int> dsu_parent;
std::vector<int> dsu_rank;
DSU() {}
explicit DSU(int n) { init(n); }
void init(int n) {
dsu_parent.resize(n + 1);
dsu_rank.assign(n + 1, 0);
for (int i = 0; i <= n; ++i) dsu_parent[i] = i;
}
int find(int x) {
while (dsu_parent[x] != x) {
dsu_parent[x] = dsu_parent[dsu_parent[x]];
x = dsu_parent[x];
}
return x;
}
bool unite(int a, int b) {
a = find(a);
b = find(b);
if (a == b) return false;
if (dsu_rank[a] < dsu_rank[b]) std::swap(a, b);
dsu_parent[b] = a;
if (dsu_rank[a] == dsu_rank[b]) ++dsu_rank[a];
return true;
}
bool same(int a, int b) { return find(a) == find(b); }
};

61
Graph.h Normal file
View File

@ -0,0 +1,61 @@
#pragma once
#include <vector>
#include <set>
#include <string>
#include <utility>
#include <tuple>
class Graph {
public:
Graph();
explicit Graph(int n);
Graph(int n, bool directed, bool weighted, char form);
int size() const { return n; }
bool isDirected() const { return directed; }
bool isWeighted() const { return weighted; }
char repr() const { return form; }
void readGraph(std::string fileName);
void writeGraph(std::string fileName);
void addEdge(int from, int to, int weight);
void removeEdge(int from, int to);
int changeEdge(int from, int to, int newWeight);
void transformToAdjList();
void transformToAdjMatrix();
void transformToListOfEdges();
Graph getSpaingTreePrima() const;
Graph getSpaingTreeKruscal() const;
Graph getSpaingTreeBoruvka() const;
int checkEuler(bool& circleExist) const;
std::vector<int> getEuleranTourFleri() const;
std::vector<int> getEuleranTourEffective() const;
int checkBipart(std::vector<char>& marks) const;
std::vector<std::pair<int, int>> getMaximumMatchingBipart() const;
Graph flowFordFulkerson(int source, int sink) const;
Graph flowDinitz(int source, int sink) const;
private:
int n = 0;
bool directed = false;
bool weighted = false;
char form = 'C';
std::vector<std::vector<int>> matrix;
std::vector<std::set<std::pair<int, int>>> adj;
std::vector<std::tuple<int, int, int>> edges;
bool hasEdge(int from, int to) const;
int weightOf(int from, int to) const;
std::vector<std::set<std::pair<int, int>>> toAdjList() const;
std::vector<std::vector<int>> toMatrix() const;
std::vector<std::tuple<int, int, int>> toEdgeList() const;
std::vector<std::set<int>> toSimpleAdj() const;
};

146
Graph_Euler.cpp Normal file
View File

@ -0,0 +1,146 @@
#include "Graph.h"
#include "DSU.h"
#include <stack>
#include <algorithm>
#include <climits>
static bool reachableWithoutEdge(const std::vector<std::set<std::pair<int, int>>>& g,
int from, int to, int a, int b, int w) {
std::vector<char> visited(g.size(), 0);
std::stack<int> st;
st.push(from);
visited[from] = 1;
while (!st.empty()) {
int u = st.top();
st.pop();
for (auto& e : g[u]) {
int v = e.first, ew = e.second;
if (u == a && v == b && ew == w) continue;
if (u == b && v == a && ew == w) continue;
if (!visited[v]) {
visited[v] = 1;
st.push(v);
}
}
}
return visited[to] != 0;
}
static bool isBridge(const std::vector<std::set<std::pair<int, int>>>& g, int a, int b, int w) {
return !reachableWithoutEdge(g, a, b, a, b, w);
}
int Graph::checkEuler(bool& circleExist) const {
circleExist = false;
if (directed) return 0;
if (n == 0) return 0;
auto g = toAdjList();
std::vector<int> deg(n + 1, 0);
DSU dsu(n);
for (int u = 1; u <= n; ++u)
for (auto& e : g[u]) {
++deg[u];
dsu.unite(u, e.first);
}
int start = 0;
for (int u = 1; u <= n; ++u)
if (deg[u] > 0) {
start = u;
break;
}
if (start == 0) { // no edges: the empty graph admits an Euler circuit
circleExist = true;
return 1;
}
for (int u = 1; u <= n; ++u)
if (deg[u] > 0 && dsu.find(u) != dsu.find(start)) return 0;
int odd = 0, oddV = 0;
for (int u = 1; u <= n; ++u)
if (deg[u] % 2) {
++odd;
oddV = u;
}
if (odd == 0) {
circleExist = true;
return start;
}
if (odd == 2) return oddV;
return 0;
}
std::vector<int> Graph::getEuleranTourFleri() const {
std::vector<int> tour;
bool circleExist = false;
int start = checkEuler(circleExist);
if (start == 0) return tour;
auto g = toAdjList();
int total = 0;
for (int u = 1; u <= n; ++u) total += (int)g[u].size();
total /= 2;
int cur = start;
tour.push_back(cur);
for (int step = 0; step < total; ++step) {
int next = -1, nextW = 0;
for (auto& e : g[cur]) {
if (!isBridge(g, cur, e.first, e.second)) {
next = e.first;
nextW = e.second;
break;
}
if (next == -1) { // remember a bridge as fallback only
next = e.first;
nextW = e.second;
}
}
if (next == -1) {
tour.clear();
return tour;
}
g[cur].erase({next, nextW});
g[next].erase({cur, nextW});
cur = next;
tour.push_back(cur);
}
return tour;
}
std::vector<int> Graph::getEuleranTourEffective() const {
std::vector<int> tour;
bool circleExist = false;
int start = checkEuler(circleExist);
if (start == 0) return tour;
auto g = toAdjList();
long long total = 0;
for (int u = 1; u <= n; ++u) total += (long long)g[u].size();
total /= 2;
std::vector<int> order;
std::stack<int> st;
st.push(start);
long long used = 0;
while (!st.empty()) {
int u = st.top();
if (g[u].empty()) {
order.push_back(u);
st.pop();
} else {
auto it = g[u].begin();
int v = it->first, w = it->second;
g[u].erase(it);
auto jt = g[v].lower_bound({u, INT_MIN});
if (jt != g[v].end() && jt->first == u && jt->second == w) g[v].erase(jt);
st.push(v);
++used;
}
}
if (used != total) return std::vector<int>();
std::reverse(order.begin(), order.end());
return order;
}

123
Graph_Flow.cpp Normal file
View File

@ -0,0 +1,123 @@
#include "Graph.h"
#include <queue>
#include <map>
#include <algorithm>
#include <functional>
#include <limits>
namespace {
struct FlowEdge {
int to;
long long cap;
int rev;
};
using Residual = std::vector<std::vector<FlowEdge>>;
void addArc(Residual& g, int u, int v, long long cap) {
FlowEdge a{v, cap, (int)g[v].size()};
FlowEdge b{u, 0, (int)g[u].size()};
g[u].push_back(a);
g[v].push_back(b);
}
Residual buildResidual(int n, const std::vector<std::tuple<int, int, int>>& arcs) {
Residual g(n + 1);
for (auto& e : arcs) addArc(g, std::get<0>(e), std::get<1>(e), std::get<2>(e));
return g;
}
// Collapse parallel arcs by summing residual capacities so the result stays a simple graph.
Graph makeResidualGraph(int n, const Residual& g) {
std::map<std::pair<int, int>, long long> cap;
for (int u = 1; u <= n; ++u)
for (auto& e : g[u])
if (e.cap > 0) cap[{u, e.to}] += e.cap;
Graph res(n, true, true, 'E');
for (auto& pr : cap)
res.addEdge(pr.first.first, pr.first.second, (int)pr.second);
return res;
}
} // namespace
Graph Graph::flowFordFulkerson(int source, int sink) const {
Residual g = buildResidual(n, toEdgeList());
std::vector<int> parent(n + 1), parentEdge(n + 1);
while (true) {
std::fill(parent.begin(), parent.end(), -1);
parent[source] = source;
std::queue<int> q;
q.push(source);
while (!q.empty()) {
int u = q.front();
q.pop();
for (int i = 0; i < (int)g[u].size(); ++i) {
const FlowEdge& e = g[u][i];
if (e.cap > 0 && parent[e.to] == -1) {
parent[e.to] = u;
parentEdge[e.to] = i;
q.push(e.to);
}
}
}
if (parent[sink] == -1) break;
long long bottleneck = std::numeric_limits<long long>::max();
for (int v = sink; v != source; v = parent[v])
bottleneck = std::min(bottleneck, g[parent[v]][parentEdge[v]].cap);
for (int v = sink; v != source; v = parent[v]) {
FlowEdge& e = g[parent[v]][parentEdge[v]];
e.cap -= bottleneck;
g[v][e.rev].cap += bottleneck;
}
}
return makeResidualGraph(n, g);
}
Graph Graph::flowDinitz(int source, int sink) const {
Residual g = buildResidual(n, toEdgeList());
std::vector<int> level(n + 1), ptr(n + 1);
auto bfs = [&]() -> bool {
std::fill(level.begin(), level.end(), -1);
level[source] = 0;
std::queue<int> q;
q.push(source);
while (!q.empty()) {
int u = q.front();
q.pop();
for (auto& e : g[u])
if (e.cap > 0 && level[e.to] == -1) {
level[e.to] = level[u] + 1;
q.push(e.to);
}
}
return level[sink] != -1;
};
std::function<long long(int, long long)> dfs = [&](int u, long long pushed) -> long long {
if (pushed == 0) return 0;
if (u == sink) return pushed;
for (int& i = ptr[u]; i < (int)g[u].size(); ++i) {
FlowEdge& e = g[u][i];
if (level[e.to] != level[u] + 1 || e.cap <= 0) continue;
long long pushedFlow = dfs(e.to, std::min(pushed, e.cap));
if (pushedFlow == 0) continue;
e.cap -= pushedFlow;
g[e.to][e.rev].cap += pushedFlow;
return pushedFlow;
}
return 0;
};
while (bfs()) {
std::fill(ptr.begin(), ptr.end(), 0);
while (dfs(source, std::numeric_limits<long long>::max()) > 0) {
}
}
return makeResidualGraph(n, g);
}

321
Graph_IO.cpp Normal file
View File

@ -0,0 +1,321 @@
#include "Graph.h"
#include <fstream>
#include <sstream>
#include <algorithm>
#include <climits>
Graph::Graph() : n(0), directed(false), weighted(false), form('C') {
matrix.assign(1, std::vector<int>(1, 0));
}
Graph::Graph(int n_) : n(n_), directed(false), weighted(true), form('L') {
adj.assign(n + 1, std::set<std::pair<int, int>>());
}
Graph::Graph(int n_, bool directed_, bool weighted_, char form_)
: n(n_), directed(directed_), weighted(weighted_), form(form_) {
if (form == 'C') matrix.assign(n + 1, std::vector<int>(n + 1, 0));
else if (form == 'L') adj.assign(n + 1, std::set<std::pair<int, int>>());
}
void Graph::readGraph(std::string fileName) {
std::ifstream in(fileName);
if (!in.is_open()) return;
char t;
in >> t;
form = t;
int m = 0;
if (form == 'E') in >> n >> m;
else in >> n;
int d, w;
in >> d >> w;
directed = (d == 1);
weighted = (w == 1);
matrix.clear();
adj.clear();
edges.clear();
if (form == 'C') {
matrix.assign(n + 1, std::vector<int>(n + 1, 0));
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j) in >> matrix[i][j];
if (!directed) {
for (int i = 1; i <= n; ++i)
for (int j = i + 1; j <= n; ++j) {
if (matrix[i][j] == 0) matrix[i][j] = matrix[j][i];
if (matrix[j][i] == 0) matrix[j][i] = matrix[i][j];
}
}
} else if (form == 'L') {
adj.assign(n + 1, std::set<std::pair<int, int>>());
std::string line;
std::getline(in, line); // rest of the "D W" line
for (int i = 1; i <= n; ++i) {
std::getline(in, line);
std::istringstream ss(line);
if (weighted) {
int v, wgt;
while (ss >> v >> wgt) adj[i].insert({v, wgt});
} else {
int v;
while (ss >> v) adj[i].insert({v, 1});
}
}
if (!directed) {
for (int u = 1; u <= n; ++u) {
std::vector<std::pair<int, int>> snapshot(adj[u].begin(), adj[u].end());
for (auto& pr : snapshot) adj[pr.first].insert({u, pr.second});
}
}
} else { // 'E'
edges.reserve(m);
std::set<std::pair<int, int>> seen;
for (int i = 0; i < m; ++i) {
int a, b, wgt = 1;
in >> a >> b;
if (weighted) in >> wgt;
if (!directed) {
auto key = std::minmax(a, b);
if (!seen.insert({key.first, key.second}).second) continue;
}
edges.push_back({a, b, wgt});
}
}
}
void Graph::writeGraph(std::string fileName) {
std::ofstream out(fileName);
if (!out.is_open()) return;
if (form == 'E') out << "E " << n << ' ' << edges.size() << '\n';
else out << form << ' ' << n << '\n';
out << (directed ? 1 : 0) << ' ' << (weighted ? 1 : 0) << '\n';
if (form == 'C') {
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) out << matrix[i][j] << ' ';
out << '\n';
}
} else if (form == 'L') {
for (int i = 1; i <= n; ++i) {
bool first = true;
for (auto& e : adj[i]) {
if (!first) out << ' ';
first = false;
out << e.first;
if (weighted) out << ' ' << e.second;
}
out << '\n';
}
} else {
for (auto& e : edges) {
out << std::get<0>(e) << ' ' << std::get<1>(e);
if (weighted) out << ' ' << std::get<2>(e);
out << '\n';
}
}
out << '\n';
}
bool Graph::hasEdge(int from, int to) const {
if (from < 1 || from > n || to < 1 || to > n) return false;
if (form == 'C') return matrix[from][to] != 0;
if (form == 'L') {
auto it = adj[from].lower_bound({to, INT_MIN});
return it != adj[from].end() && it->first == to;
}
for (auto& e : edges)
if ((std::get<0>(e) == from && std::get<1>(e) == to) ||
(!directed && std::get<0>(e) == to && std::get<1>(e) == from))
return true;
return false;
}
int Graph::weightOf(int from, int to) const {
if (from < 1 || from > n || to < 1 || to > n) return 0;
if (form == 'C') return matrix[from][to];
if (form == 'L') {
auto it = adj[from].lower_bound({to, INT_MIN});
if (it != adj[from].end() && it->first == to) return it->second;
return 0;
}
for (auto& e : edges)
if ((std::get<0>(e) == from && std::get<1>(e) == to) ||
(!directed && std::get<0>(e) == to && std::get<1>(e) == from))
return std::get<2>(e);
return 0;
}
void Graph::addEdge(int from, int to, int weight) {
if (!weighted) weight = 1;
if (hasEdge(from, to)) return;
if (form == 'C') {
matrix[from][to] = weight;
if (!directed) matrix[to][from] = weight;
} else if (form == 'L') {
adj[from].insert({to, weight});
if (!directed) adj[to].insert({from, weight});
} else {
edges.push_back({from, to, weight});
}
}
void Graph::removeEdge(int from, int to) {
if (form == 'C') {
if (matrix[from][to] == 0) return;
matrix[from][to] = 0;
if (!directed) matrix[to][from] = 0;
} else if (form == 'L') {
auto it = adj[from].lower_bound({to, INT_MIN});
if (it != adj[from].end() && it->first == to) adj[from].erase(it);
if (!directed) {
auto jt = adj[to].lower_bound({from, INT_MIN});
if (jt != adj[to].end() && jt->first == from) adj[to].erase(jt);
}
} else {
for (auto it = edges.begin(); it != edges.end(); ++it) {
bool match = (std::get<0>(*it) == from && std::get<1>(*it) == to) ||
(!directed && std::get<0>(*it) == to && std::get<1>(*it) == from);
if (match) {
edges.erase(it);
break;
}
}
}
}
int Graph::changeEdge(int from, int to, int newWeight) {
if (!weighted) newWeight = 1;
if (form == 'C') {
int old = matrix[from][to];
if (old == 0) return 0;
matrix[from][to] = newWeight;
if (!directed) matrix[to][from] = newWeight;
return old;
}
if (form == 'L') {
auto it = adj[from].lower_bound({to, INT_MIN});
if (it == adj[from].end() || it->first != to) return 0;
int old = it->second;
adj[from].erase(it);
adj[from].insert({to, newWeight});
if (!directed) {
auto jt = adj[to].lower_bound({from, INT_MIN});
if (jt != adj[to].end() && jt->first == from) {
adj[to].erase(jt);
adj[to].insert({from, newWeight});
}
}
return old;
}
for (auto& e : edges) {
bool match = (std::get<0>(e) == from && std::get<1>(e) == to) ||
(!directed && std::get<0>(e) == to && std::get<1>(e) == from);
if (match) {
int old = std::get<2>(e);
std::get<2>(e) = newWeight;
return old;
}
}
return 0;
}
std::vector<std::set<std::pair<int, int>>> Graph::toAdjList() const {
if (form == 'L') return adj;
std::vector<std::set<std::pair<int, int>>> g(n + 1);
if (form == 'C') {
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j) {
if (i == j || matrix[i][j] == 0) continue;
g[i].insert({j, matrix[i][j]});
if (!directed) g[j].insert({i, matrix[i][j]});
}
} else {
for (auto& e : edges) {
int a = std::get<0>(e), b = std::get<1>(e), w = std::get<2>(e);
g[a].insert({b, w});
if (!directed) g[b].insert({a, w});
}
}
return g;
}
std::vector<std::vector<int>> Graph::toMatrix() const {
if (form == 'C') return matrix;
std::vector<std::vector<int>> M(n + 1, std::vector<int>(n + 1, 0));
if (form == 'L') {
for (int u = 1; u <= n; ++u)
for (auto& e : adj[u]) {
M[u][e.first] = e.second;
if (!directed) M[e.first][u] = e.second;
}
} else {
for (auto& e : edges) {
int a = std::get<0>(e), b = std::get<1>(e), w = std::get<2>(e);
M[a][b] = w;
if (!directed) M[b][a] = w;
}
}
return M;
}
std::vector<std::tuple<int, int, int>> Graph::toEdgeList() const {
if (form == 'E') return edges;
std::vector<std::tuple<int, int, int>> el;
if (form == 'L') {
for (int u = 1; u <= n; ++u)
for (auto& e : adj[u]) {
if (!directed && e.first < u) continue;
el.push_back({u, e.first, e.second});
}
} else {
for (int i = 1; i <= n; ++i)
for (int j = (directed ? 1 : i); j <= n; ++j) {
if (matrix[i][j] == 0) continue;
el.push_back({i, j, matrix[i][j]});
}
}
return el;
}
std::vector<std::set<int>> Graph::toSimpleAdj() const {
auto g = toAdjList();
std::vector<std::set<int>> nb(n + 1);
for (int u = 1; u <= n; ++u)
for (auto& e : g[u]) {
nb[u].insert(e.first);
nb[e.first].insert(u);
}
return nb;
}
void Graph::transformToAdjList() {
if (form == 'L') return;
adj = toAdjList();
matrix.clear();
edges.clear();
form = 'L';
}
void Graph::transformToAdjMatrix() {
if (form == 'C') return;
matrix = toMatrix();
adj.clear();
edges.clear();
form = 'C';
}
void Graph::transformToListOfEdges() {
if (form == 'E') return;
edges = toEdgeList();
matrix.clear();
adj.clear();
form = 'E';
}

86
Graph_MST.cpp Normal file
View File

@ -0,0 +1,86 @@
#include "Graph.h"
#include "DSU.h"
#include <queue>
#include <functional>
#include <algorithm>
#include <limits>
Graph Graph::getSpaingTreePrima() const {
Graph mst(n, false, true, 'L');
auto g = toAdjList();
const long long INF = std::numeric_limits<long long>::max();
std::vector<long long> minEdge(n + 1, INF);
std::vector<int> parent(n + 1, 0);
std::vector<char> used(n + 1, 0);
using Node = std::pair<long long, int>;
std::priority_queue<Node, std::vector<Node>, std::greater<Node>> pq;
for (int s = 1; s <= n; ++s) {
if (used[s]) continue;
minEdge[s] = 0;
pq.push({0, s});
while (!pq.empty()) {
auto [w, u] = pq.top();
pq.pop();
if (used[u]) continue;
used[u] = 1;
if (parent[u] != 0) mst.addEdge(u, parent[u], (int)w);
for (auto& e : g[u]) {
int v = e.first, wgt = e.second;
if (!used[v] && wgt < minEdge[v]) {
minEdge[v] = wgt;
parent[v] = u;
pq.push({wgt, v});
}
}
}
}
return mst;
}
Graph Graph::getSpaingTreeKruscal() const {
Graph mst(n, false, true, 'L');
DSU dsu(n);
auto el = toEdgeList();
std::sort(el.begin(), el.end(), [](const auto& a, const auto& b) {
return std::get<2>(a) < std::get<2>(b);
});
for (auto& e : el) {
int a = std::get<0>(e), b = std::get<1>(e), w = std::get<2>(e);
if (dsu.unite(a, b)) mst.addEdge(a, b, w);
}
return mst;
}
Graph Graph::getSpaingTreeBoruvka() const {
Graph mst(n, false, true, 'L');
DSU dsu(n);
auto el = toEdgeList();
int comp = n;
while (comp > 1) {
std::vector<int> best(n + 1, -1);
for (int i = 0; i < (int)el.size(); ++i) {
int ra = dsu.find(std::get<0>(el[i]));
int rb = dsu.find(std::get<1>(el[i]));
if (ra == rb) continue;
int w = std::get<2>(el[i]);
if (best[ra] == -1 || w < std::get<2>(el[best[ra]])) best[ra] = i;
if (best[rb] == -1 || w < std::get<2>(el[best[rb]])) best[rb] = i;
}
bool merged = false;
for (int v = 1; v <= n; ++v) {
if (best[v] == -1) continue;
auto& e = el[best[v]];
if (dsu.unite(std::get<0>(e), std::get<1>(e))) {
mst.addEdge(std::get<0>(e), std::get<1>(e), std::get<2>(e));
--comp;
merged = true;
}
}
if (!merged) break;
}
return mst;
}

61
Graph_Matching.cpp Normal file
View File

@ -0,0 +1,61 @@
#include "Graph.h"
#include <queue>
#include <algorithm>
#include <functional>
int Graph::checkBipart(std::vector<char>& marks) const {
marks.assign(n + 1, 0);
auto nb = toSimpleAdj();
std::queue<int> q;
for (int s = 1; s <= n; ++s) {
if (marks[s]) continue;
marks[s] = 'A';
q.push(s);
while (!q.empty()) {
int u = q.front();
q.pop();
char opposite = (marks[u] == 'A') ? 'B' : 'A';
for (int v : nb[u]) {
if (marks[v] == 0) {
marks[v] = opposite;
q.push(v);
} else if (marks[v] == marks[u]) {
return 0;
}
}
}
}
return 1;
}
std::vector<std::pair<int, int>> Graph::getMaximumMatchingBipart() const {
std::vector<std::pair<int, int>> matching;
std::vector<char> marks;
if (!checkBipart(marks)) return matching;
auto nb = toSimpleAdj();
std::vector<int> match(n + 1, 0); // right vertex -> matched left vertex
std::vector<char> used(n + 1, 0);
std::function<bool(int)> augment = [&](int u) -> bool {
used[u] = 1;
for (int v : nb[u]) {
if (marks[v] != 'B') continue;
if (match[v] == 0 || (!used[match[v]] && augment(match[v]))) {
match[v] = u;
return true;
}
}
return false;
};
for (int u = 1; u <= n; ++u) {
if (marks[u] != 'A') continue;
std::fill(used.begin(), used.end(), (char)0);
augment(u);
}
for (int v = 1; v <= n; ++v)
if (match[v] != 0) matching.push_back({match[v], v});
return matching;
}

0
README.md Normal file
View File

24
main.cpp Normal file
View File

@ -0,0 +1,24 @@
#include "Graph.h"
#include <iostream>
int main(int argc, char* argv[]) {
if (argc < 3) return 0;
Graph g;
g.readGraph(argv[1]);
if (g.size() == 0) return 0;
if (!g.isDirected() && g.isWeighted()) {
g.getSpaingTreeKruscal().writeGraph(argv[2]);
} else if (g.isDirected() && g.isWeighted()) {
g.flowDinitz(1, g.size()).writeGraph(argv[2]);
} else {
bool circleExist = false;
int start = g.checkEuler(circleExist);
if (start != 0) {
for (int v : g.getEuleranTourEffective()) std::cout << v << ' ';
std::cout << '\n';
}
}
return 0;
}