-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdistance.h
143 lines (117 loc) · 3.87 KB
/
distance.h
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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
/// 本文件实现了一个距离矩阵的存储.
/// 由于距离矩阵是对称的,因此只需要存储一半的距离即可.
#pragma once
#include <vector>
#include <unordered_map>
#include <iostream>
#include "graph.h"
template <typename Weight>
class DistanceMatrix {
std::unordered_map<Vertex, Weight> diagonal;
std::vector<Weight> distances;
std::unordered_map<Vertex, size_t> vertex_to_index;
size_t last_index = 0;
size_t get_index(Vertex vertex) const {
return vertex_to_index.at(vertex);
}
optional<size_t> get_index_opt(Vertex vertex) const {
if (!vertex_to_index.contains(vertex)) {
return nullopt;
}
return vertex_to_index.at(vertex);
}
size_t get_index_mut(Vertex vertex) {
if (!vertex_to_index.contains(vertex)) {
vertex_to_index[vertex] = last_index++;
}
return vertex_to_index[vertex];
}
// 计算两点距离在距离矩阵中的位置
static size_t calc_pos(size_t i, size_t j) {
if (i > j) std::swap(i, j);
return j * (j - 1) / 2 + i;
}
public:
const Weight inf = ~0;
const bool self_circle = false;
using T = Weight;
DistanceMatrix(size_t n, const bool self_circle = false, const Weight inf = ~0) : distances(n * (n - 1) / 2, inf), self_circle(self_circle), inf(inf) {}
void set(Vertex i, Vertex j, Weight distance) {
if (i == j) {
if (self_circle) {
// 给顶点 i 在矩阵上申请一个位置,占位
const auto _ = get_index_mut(i);
// 对角线元素放到对角线的 map 中
diagonal[i] = distance;
} else {
throw std::invalid_argument("self circle is not allowed.");
}
} else {
size_t pos = calc_pos(get_index_mut(i), get_index_mut(j));
distances[pos] = distance;
}
}
Weight get(Vertex i, Vertex j) const {
if (i == j) {
if (!self_circle) return 0;
return diagonal.at(i); /* 可能产生异常 */
}
size_t pos = calc_pos(get_index(i), get_index(j));
return distances[pos];
}
Weight get_or_inf(Vertex i, Vertex j) const {
if (i == j) {
if (!self_circle) return 0;
if (const auto it = diagonal.cfind(i); it != diagonal.cend()) return it->second;
return inf;
}
optional<size_t> pos_i, pos_j;
if ((pos_i = get_index_opt(i)) == nullopt || (pos_j = get_index_opt(j)) == nullopt) {
return this->inf;
}
const auto pos = calc_pos(*pos_i, *pos_j);
return distances[pos];
}
Weight operator()(Vertex i, Vertex j) const {
return get(i, j);
}
size_t size() const {
return distances.size();
}
};
template <typename W>
void print_distance_matrix(const Graph &g, const DistanceMatrix<W> &matrix) {
// print header line
std::cout << " ";
for (const Vertex i: g.vertices) {
std::cout << i << " ";
}
std::cout << std::endl << "--------------------------------" << std::endl;
for (const Vertex i: g.vertices) {
std::cout << i << " | ";
for (const Vertex j: g.vertices) {
std::cout << matrix.get(i, j) << " ";
}
std::cout << std::endl;
}
}
template <typename W>
struct DistanceVector
{
const W inf = ~0;
Vertex source;
std::unordered_map<Vertex, W> distances;
explicit DistanceVector(const Vertex source): source(source) {}
W operator[](const Vertex target) const {
return get(target);
}
void set(const Vertex target, W weight) {
this->distances[target] = weight;
}
W get(const Vertex target) const {
if (auto it = distances.find(target); it != distances.end()) {
return it->second;
}
return inf;
}
};