Add README
This commit is contained in:
commit
4989641538
37
DSU.h
Normal file
37
DSU.h
Normal 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
61
Graph.h
Normal 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
146
Graph_Euler.cpp
Normal 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
123
Graph_Flow.cpp
Normal 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
321
Graph_IO.cpp
Normal 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
86
Graph_MST.cpp
Normal 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
61
Graph_Matching.cpp
Normal 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;
|
||||||
|
}
|
||||||
24
main.cpp
Normal file
24
main.cpp
Normal 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;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user