forked from google/or-tools
-
Notifications
You must be signed in to change notification settings - Fork 2
/
range_minimum_query.h
178 lines (153 loc) · 6.57 KB
/
range_minimum_query.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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
// Copyright 2010-2024 Google LLC
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// We use the notation min(arr, i, j) for the minimum arr[x] such that i <= x
// and x < j.
// Range Minimum Query (RMQ) is a data structure preprocessing an array arr so
// that querying min(arr, i, j) takes O(1) time. The preprocessing takes
// O(n*log(n)) time and memory.
// Note: There exists an O(n) preprocessing algorithm, but it is considerably
// more involved and the hidden constants behind it are much higher.
//
// The algorithms are well explained in Wikipedia:
// https://en.wikipedia.org/wiki/Range_minimum_query.
//
//
// Implementation: The idea is to cache every min(arr, i, j) where j - i is a
// power of two, i.e. j = i + 2^k for some k. Provided this information, we can
// answer all queries in O(1): given a pair (i, j) find the maximum k such that
// i + 2^k < j and note that
// std::min(min(arr, i, i+2^k), min(arr, j-2^k, j)) = min(arr, i, j).
#ifndef OR_TOOLS_UTIL_RANGE_MINIMUM_QUERY_H_
#define OR_TOOLS_UTIL_RANGE_MINIMUM_QUERY_H_
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <vector>
#include "ortools/util/bitset.h"
namespace operations_research {
template <typename T, typename Compare = std::less<T>>
class RangeMinimumQuery {
public:
explicit RangeMinimumQuery(std::vector<T> array);
RangeMinimumQuery(std::vector<T> array, Compare cmp);
// This type is neither copyable nor movable.
RangeMinimumQuery(const RangeMinimumQuery&) = delete;
RangeMinimumQuery& operator=(const RangeMinimumQuery&) = delete;
// Returns the minimum (w.r.t. Compare) arr[x], where x is contained in
// [from, to).
T GetMinimumFromRange(int from, int to) const;
const std::vector<T>& array() const;
private:
// cache_[k][i] = min(arr, i, i+2^k).
std::vector<std::vector<T>> cache_;
Compare cmp_;
};
// RangeMinimumIndexQuery is similar to RangeMinimumQuery, but
// GetMinimumIndexFromRange returns the index for which the minimum is attained.
template <typename T, typename Compare = std::less<T>>
class RangeMinimumIndexQuery {
public:
explicit RangeMinimumIndexQuery(std::vector<T> array);
RangeMinimumIndexQuery(std::vector<T> array, Compare cmp);
// This type is neither copyable nor movable.
RangeMinimumIndexQuery(const RangeMinimumIndexQuery&) = delete;
RangeMinimumIndexQuery& operator=(const RangeMinimumIndexQuery&) = delete;
// Returns an index idx from [from, to) such that arr[idx] is the minimum
// value of arr over the interval [from, to).
int GetMinimumIndexFromRange(int from, int to) const;
// Returns the original array.
const std::vector<T>& array() const;
private:
// Returns a vector with values 0, 1, ... n - 1 for a given n.
static std::vector<int> CreateIndexVector(int n);
struct IndexComparator {
bool operator()(int lhs_idx, int rhs_idx) const;
const std::vector<T> array;
Compare cmp;
} cmp_;
const RangeMinimumQuery<int, IndexComparator> rmq_;
};
// RangeMinimumQuery implementation
template <typename T, typename Compare>
inline RangeMinimumQuery<T, Compare>::RangeMinimumQuery(std::vector<T> array)
: RangeMinimumQuery(std::move(array), Compare()) {}
// Reminder: The task is to fill cache_ so that
// cache_[k][i] = min(arr, i, i+2^k) for every k <= Log2(n) and i <= n-2^k.
// Note that cache_[k+1][i] = min(cache_[k][i], cache_[k][i+2^k]), hence every
// row can be efficiently computed from the previous.
template <typename T, typename Compare>
RangeMinimumQuery<T, Compare>::RangeMinimumQuery(std::vector<T> array,
Compare cmp)
: cache_(MostSignificantBitPosition32(array.size()) + 1),
cmp_(std::move(cmp)) {
const int array_size = array.size();
cache_[0] = std::move(array);
for (int row_idx = 1; row_idx < cache_.size(); ++row_idx) {
const int row_length = array_size - (1 << row_idx) + 1;
const int window = 1 << (row_idx - 1);
cache_[row_idx].resize(row_length);
for (int col_idx = 0; col_idx < row_length; ++col_idx) {
cache_[row_idx][col_idx] =
std::min(cache_[row_idx - 1][col_idx],
cache_[row_idx - 1][col_idx + window], cmp_);
}
}
}
template <typename T, typename Compare>
inline T RangeMinimumQuery<T, Compare>::GetMinimumFromRange(int from,
int to) const {
DCHECK_LE(0, from);
DCHECK_LT(from, to);
DCHECK_LE(to, array().size());
const int log_diff = MostSignificantBitPosition32(to - from);
const int window = 1 << log_diff;
const std::vector<T>& row = cache_[log_diff];
return std::min(row[from], row[to - window], cmp_);
}
template <typename T, typename Compare>
inline const std::vector<T>& RangeMinimumQuery<T, Compare>::array() const {
return cache_[0];
}
// RangeMinimumIndexQuery implementation
template <typename T, typename Compare>
inline RangeMinimumIndexQuery<T, Compare>::RangeMinimumIndexQuery(
std::vector<T> array)
: RangeMinimumIndexQuery(std::move(array), Compare()) {}
template <typename T, typename Compare>
RangeMinimumIndexQuery<T, Compare>::RangeMinimumIndexQuery(std::vector<T> array,
Compare cmp)
: cmp_({std::move(array), std::move(cmp)}),
rmq_(CreateIndexVector(cmp_.array.size()), cmp_) {}
template <typename T, typename Compare>
inline int RangeMinimumIndexQuery<T, Compare>::GetMinimumIndexFromRange(
int from, int to) const {
return rmq_.GetMinimumFromRange(from, to);
}
template <typename T, typename Compare>
inline bool RangeMinimumIndexQuery<T, Compare>::IndexComparator::operator()(
int lhs_idx, int rhs_idx) const {
return cmp(array[lhs_idx], array[rhs_idx]);
}
template <typename T, typename Compare>
std::vector<int> RangeMinimumIndexQuery<T, Compare>::CreateIndexVector(int n) {
std::vector<int> result(n, 0);
std::iota(result.begin(), result.end(), 0);
return result;
}
template <typename T, typename Compare>
inline const std::vector<T>& RangeMinimumIndexQuery<T, Compare>::array() const {
return cmp_.array;
}
} // namespace operations_research
#endif // OR_TOOLS_UTIL_RANGE_MINIMUM_QUERY_H_