#ifndef _BF_HPP #define _BF_HPP #include #include #include #include #include "graph.hpp" const static std::vector DIRECTIONS = std::vector{std::make_pair(0, 1), std::make_pair(0, -1), std::make_pair(1, 0), std::make_pair(-1, 0)}; static auto _check_vertex(const graph& g, std::vector>& distances, int v, bool check_only = false) -> bool { bool improvement_found = false; // unpack the vertex coordinates int y = v / g.width(); int x = v % g.width(); // skip the cells we cannot reach if (distances[y][x] == graph::unreachable()) { return false; } // go through the neighbours auto u = std::make_pair(x, y); for (const auto& [dx, dy] : DIRECTIONS) { auto v = std::make_pair(x + dx, y + dy); auto cost = g.cost(u, v); // if we can move to the cell and it's better, relax¹ it if (cost != graph::unreachable() && distances[y][x] + cost < distances[y + dy][x + dx]) { if (check_only) { return true; } distances[y + dy][x + dx] = distances[y][x] + cost; improvement_found = true; } } return improvement_found; } auto bf(const graph& g, const vertex_t& source, const vertex_t& destination) -> int { // ‹source› must be within the bounds assert(g.has(source)); // ‹destination› must be within the bounds assert(g.has(destination)); // we need to initialize the distances std::vector> distances( g.height(), std::vector(g.width(), graph::unreachable())); // ‹source› destination denotes the beginning where the cost is 0 auto [sx, sy] = source; distances[sy][sx] = 0; // now we need to improve the paths as long as possible bool improvement_found; do { // reset the flag at the beginning improvement_found = false; // go through all of the vertices for (int v = g.height() * g.width() - 1; v >= 0; --v) { improvement_found = _check_vertex(g, distances, v) || improvement_found; } } while (improvement_found); return distances[destination.second][destination.first]; } auto bf_finite(const graph& g, const vertex_t& source, const vertex_t& destination) -> int { // ‹source› must be within the bounds assert(g.has(source)); // ‹destination› must be within the bounds assert(g.has(destination)); // we need to initialize the distances std::vector> distances( g.height(), std::vector(g.width(), graph::unreachable())); // ‹source› destination denotes the beginning where the cost is 0 auto [sx, sy] = source; distances[sy][sx] = 0; // now we only iterate as many times as cells that we have for (int i = g.height() * g.width(); i > 0; --i) { // go through all of the vertices for (int v = g.height() * g.width() - 1; v >= 0; --v) { _check_vertex(g, distances, v); } } return distances[destination.second][destination.first]; } auto bellman_ford(const graph& g, const vertex_t& source) -> std::vector> { // ‹source› must be within the bounds assert(g.has(source)); // we need to initialize the distances std::vector> distances( g.height(), std::vector(g.width(), graph::unreachable())); // ‹source› destination denotes the beginning where the cost is 0 auto [sx, sy] = source; distances[sy][sx] = 0; // now we only iterate as many times as cells that we have for (int i = g.height() * g.width(); i > 0; --i) { // go through all of the vertices for (int v = g.height() * g.width() - 1; v >= 0; --v) { _check_vertex(g, distances, v); } } // now we check for the negative loops for (int v = g.height() * g.width() - 1; v >= 0; --v) { if (_check_vertex(g, distances, v, true)) { std::cerr << "[Bellman-Ford] Found a negative loop\n"; break; } } return distances; } #endif /* _BF_HPP */