1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
//! 最短距離 (Dijkstra)。

use std::cmp::Reverse;
use std::collections::BinaryHeap;

/// Dijkstra 法に基づく最短距離。
///
/// # Parameters
/// - `n`: 頂点数
/// - `s`: 始点
/// - `zero`: 距離を表す型の $0$
/// - `index`: 頂点から添字への番号づけをする関数
/// - `delta`: 頂点 `v` と関数 `search` を受け取る関数
///
/// `delta` は、`v` の各隣接頂点 `nv` とそこへの距離 `ew` に対して、
/// `search(nv, ew)` を呼び出す必要がある。
///
/// # Examples
/// `g[v]` が `v` の隣接頂点を持つ、よくある隣接リストにおける例を載せる。
/// 次のようなグラフである。
///
/// ```text
///      2        3
/// (0) ---> (1) ---> (2)
///  ^        |        |
///  | 1      |        | 4
///  |        |  9     v
/// (3)       +-----> (4)
/// ```
///
/// ```
/// use nekolib::graph::dijkstra;
///
/// let g = vec![
///     vec![(1, 2)],
///     vec![(2, 3), (4, 9)],
///     vec![(4, 4)],
///     vec![(0, 1)],
///     vec![],
/// ];
/// let index = |&v: &usize| v;
/// let delta = |&v: &usize| g[v].iter().cloned();
/// let dist = dijkstra(5, 0, 0_i32, index, delta);
///
/// assert_eq!(dist, vec![Some(0), Some(2), Some(5), None, Some(9)]);
/// ```
///
/// 頂点数の上限 $n$ さえ既知であれば、`index` は動的に決められる。
/// ```
/// use std::collections::BTreeMap;
///
/// use nekolib::graph::dijkstra;
///
/// let n = 10;
///
/// let s = 10001;
/// let t = 10015;
/// let u = t - 1;
/// let mut enc = BTreeMap::new();
/// let index = |&v: &usize| match enc.get(&v) {
///     Some(&i) => i,
///     None => {
///         let len = enc.len();
///         enc.insert(v, len);
///         len
///     }
/// };
/// let delta =
///   |&v: &usize| Some((v + 2, 1)).filter(|&(v, _)| v <= t).into_iter();
///
/// let res = dijkstra(n, s, 0, index, delta);
/// assert_eq!(res[enc[&t]], Some(7));
/// assert!(!enc.contains_key(&u))
/// ```
///
/// # Notes
/// 複数のグラフを扱う際に `delta` を使い回すと、意図しないグラフを見てしまいがちなので注意。
///
/// # Complexity
/// 二分ヒープを用いる実装なので、$O(|E|\\log(|V|))$ 時間。
/// ここで、$V$ は頂点集合、$E$ は辺集合である。
///
/// # References
/// - <https://niuez.github.io/posts/impl_abstract_dijkstra/>
///     - これを意識していますが、辺の取得にイテレータを使っているので少々異なります。
pub fn dijkstra<V, W, E>(
    n: usize,
    s: V,
    zero: W,
    mut index: impl FnMut(&V) -> usize,
    mut delta: impl FnMut(&V) -> E,
) -> Vec<Option<W>>
where
    V: Ord,
    W: Ord + std::ops::Add<W, Output = W> + Clone,
    E: Iterator<Item = (V, W)>,
{
    let mut dist: Vec<Option<W>> = vec![None; n];
    let si = index(&s);
    dist[si] = Some(zero);
    let mut pq: BinaryHeap<_> =
        vec![Reverse((dist[si].clone().unwrap(), s))].into();

    while let Some(Reverse((w, v))) = pq.pop() {
        match &dist[index(&v)] {
            Some(cw) if cw < &w => continue,
            _ => {}
        }
        for (nv, ew) in delta(&v) {
            let nw = w.clone() + ew;
            match &dist[index(&nv)] {
                Some(cw) if cw <= &nw => {}
                _ => {
                    dist[index(&nv)] = Some(nw.clone());
                    pq.push(Reverse((nw, nv)));
                }
            }
        }
    }
    dist
}