Link Search Menu Expand Document
あるまかんライブラリ

:heavy_check_mark: dijkstra() (ダイクストラ, $O((V + E)\log V)$)
(Graph/Shortest-Path/dijkstra.hpp)

Depends on

Verified with

Code

#pragma once
#include <cassert>
#include <functional>
#include <queue>
#include <type_traits>
#include <utility>
#include <vector>

#include "../../Util/at.hpp"
#include "../../Util/chminmax.hpp"
#include "../../Util/int-infinity.hpp"

/**
 * @brief dijkstra() (ダイクストラ, $O((V + E)\log V)$)
 *
 * @param dist:
 *      start からの距離を格納する配列。
 *      要素数は頂点数以上でなければならない。INFなどのでかい値でfillされている必要がある。
 *      多次元配列でもOK。
 *
 * @param start:
 *      頂点を表すdistの添字。
 *      distが一次元配列なら整数型,
 *      distが二次元配列ならstd::pair<integer, integer> または std::tuple<integer, integer> または std::array<integer, 2>,
 *      distが三次元配列ならstd::tuple<integer, integer, integer> または std::array<integer, 3>。
 *      start = {i, j, k} のとき dist[i][j][k] でアクセスする。
 *
 * @param enumerateNextVertex:
 *      fn(from: Vertex, fn(to: Vertex, w: EdgeWeight) => void) => void
 *      第一引数にVertex, 第二引数に関数をとる関数。
 *      この関数が第一引数の頂点fromから次の頂点の遷移先を列挙し、第二引数に渡すことで dijkstra() に遷移処理を委譲する。
 */
template <class DistArray, class Vertex, class NextVertexEnumerator>
DistArray dijkstra(DistArray&& dist, Vertex start, NextVertexEnumerator enumerateNextVertex) {
    using arumakan::at;
    assert(!dist.empty());
    assert(at(dist, start) >= INF);
    using WeightType = typename std::remove_reference_t<decltype(at(dist, start))>;
    using P = std::pair<WeightType, Vertex>;

    std::priority_queue<P, std::vector<P>, std::greater<P>> que;
    que.emplace(at(dist, start) = WeightType(), start);

    while (!que.empty()) {
        const auto curDist = que.top().first;
        const auto curVertex = que.top().second;
        que.pop();
        if (at(dist, curVertex) < curDist) continue;
        enumerateNextVertex(curVertex, [&](auto nxtVertex, auto edgeWeight) {
            if (chmin(at(dist, nxtVertex), curDist + edgeWeight)) {
                que.emplace(curDist + edgeWeight, nxtVertex);
            }
        });
    }
    return dist;
}
#line 2 "Graph/Shortest-Path/dijkstra.hpp"
#include <cassert>
#include <functional>
#include <queue>
#include <type_traits>
#include <utility>
#include <vector>

#line 2 "Util/at.hpp"
#include <tuple>
#line 5 "Util/at.hpp"

/**
 * @brief at() ()
 */
namespace arumakan {

//! at(a, i) returns a[i]
template <class Array, class Integer, std::enable_if_t<std::is_integral<Integer>::value, std::nullptr_t> = nullptr>
inline auto at(Array&& a, Integer i) -> decltype(a[0])& {
    return a[i];
}

//! at(a, Tuple{i}) returns a[i]
template <class Array, class Tuple, std::enable_if_t<std::tuple_size<Tuple>::value == 1, std::nullptr_t> = nullptr>
inline auto at(Array&& a, Tuple index) -> decltype(a[0])& {
    return a[std::get<0>(index)];
}

//! at(mat, Tuple{y, x}) returns mat[y][x]
template <class Matrix, class Tuple, std::enable_if_t<std::tuple_size<Tuple>::value == 2, std::nullptr_t> = nullptr>
inline auto at(Matrix&& mat, Tuple index) -> decltype(mat[0][0])& {
    return mat[std::get<0>(index)][std::get<1>(index)];
}

//! at(cube, Tuple{z, y, x}) returns cube[z][y][x]
template <class Cube, class Tuple, std::enable_if_t<std::tuple_size<Tuple>::value == 3, std::nullptr_t> = nullptr>
inline auto at(Cube&& cube, Tuple index) -> decltype(cube[0][0][0])& {
    return cube[std::get<0>(index)][std::get<1>(index)][std::get<2>(index)];
}

}  // namespace arumakan
#line 2 "Util/chminmax.hpp"

/**
 * @brief chmin(), chmax()
 */
template <class T, class U>
inline bool chmin(T& a, const U& b) {
    return b < a && (a = b, true);
}

template <class T, class U>
inline bool chmax(T& a, const U& b) {
    return b > a && (a = b, true);
}
#line 2 "Util/int-infinity.hpp"
#include <cstdint>

/**
 * @brief int-infinity (整数のデカイ値)
 * 2倍してもオーバーフローしない & memset()にも使える (需要ある?)
 */
constexpr int32_t INF = 0x3f3f3f3f;
constexpr int64_t LINF = 0x3f3f3f3f3f3f3f3fLL;
#line 12 "Graph/Shortest-Path/dijkstra.hpp"

/**
 * @brief dijkstra() (ダイクストラ, $O((V + E)\log V)$)
 *
 * @param dist:
 *      start からの距離を格納する配列。
 *      要素数は頂点数以上でなければならない。INFなどのでかい値でfillされている必要がある。
 *      多次元配列でもOK。
 *
 * @param start:
 *      頂点を表すdistの添字。
 *      distが一次元配列なら整数型,
 *      distが二次元配列ならstd::pair<integer, integer> または std::tuple<integer, integer> または std::array<integer, 2>,
 *      distが三次元配列ならstd::tuple<integer, integer, integer> または std::array<integer, 3>。
 *      start = {i, j, k} のとき dist[i][j][k] でアクセスする。
 *
 * @param enumerateNextVertex:
 *      fn(from: Vertex, fn(to: Vertex, w: EdgeWeight) => void) => void
 *      第一引数にVertex, 第二引数に関数をとる関数。
 *      この関数が第一引数の頂点fromから次の頂点の遷移先を列挙し、第二引数に渡すことで dijkstra() に遷移処理を委譲する。
 */
template <class DistArray, class Vertex, class NextVertexEnumerator>
DistArray dijkstra(DistArray&& dist, Vertex start, NextVertexEnumerator enumerateNextVertex) {
    using arumakan::at;
    assert(!dist.empty());
    assert(at(dist, start) >= INF);
    using WeightType = typename std::remove_reference_t<decltype(at(dist, start))>;
    using P = std::pair<WeightType, Vertex>;

    std::priority_queue<P, std::vector<P>, std::greater<P>> que;
    que.emplace(at(dist, start) = WeightType(), start);

    while (!que.empty()) {
        const auto curDist = que.top().first;
        const auto curVertex = que.top().second;
        que.pop();
        if (at(dist, curVertex) < curDist) continue;
        enumerateNextVertex(curVertex, [&](auto nxtVertex, auto edgeWeight) {
            if (chmin(at(dist, nxtVertex), curDist + edgeWeight)) {
                que.emplace(curDist + edgeWeight, nxtVertex);
            }
        });
    }
    return dist;
}
Back to top page